2013-04-28 60 views
2

我有3个我想作为Windows服务托管的Restful服务。我创建了一个安装程序,它将把所有三项服务放在一起。我想让我的安装程序有点可配置。我想将新服务添加到同一个安装程序,而无需编辑代码。加载DLL并将其作为Windows服务托管

这是我目前的安装程序代码。在这里,我提到了dll并托管它们。这是任何Windows主机项目的普通代码。

App.config文件

<services> 
    <service name="Service1"> 
    <endpoint binding="webHttpBinding" contract="IService1" ehaviorConfiguration="REST"/> 
    </service> 
    <service name="Service2"> 
    <endpoint binding="webHttpBinding" contract="IService2" behaviorConfiguration="REST"/> 
    </service> 
    <service name="Service3"> 
    <endpoint binding="webHttpBinding" contract="IService3" behaviorConfiguration="REST"/> 
    </service> 
</services> 
<behaviors> 
    <serviceBehaviors> 
    <behavior> 
     <serviceMetadata httpGetEnabled="True"/> 
     <serviceDebug includeExceptionDetailInFaults="true"/> 
    </behavior> 
    </serviceBehaviors> 

    <endpointBehaviors> 
    <behavior name="REST"> 
     <webHttp automaticFormatSelectionEnabled="true" defaultOutgoingResponseFormat="Json" helpEnabled="true"/> 
    </behavior> 
    </endpointBehaviors> 
</behaviors> 

代码的安装程序。

public partial class Service : ServiceBase 
{ 
    public ServiceHost Service1Host = null; 
    public ServiceHost Service2Host = null; 
    public ServiceHost Service3Host = null; 

    public Service() 
    { 
     InitializeComponent(); 
    } 

    protected override void OnStart(string[] args) 
    { 


     Uri Service1_baseAddress = new Uri("http://localhost:9999/Service1"); 
     Uri Service2_baseAddress = new Uri("http://localhost:9999/Service2"); 
     Uri Service3_baseAddress = new Uri("http://localhost:9999/Service3"); 

     if (Service1Host != null) 
     { 
      Service1Host.Close(); 
     } 
     Service1Host = new ServiceHost(typeof(Service1), Service1_baseAddress);       
     Service1Host.Open(); 

     if (Service2Host != null) 
     { 
      Service2Host.Close(); 
     } 
     Service2Host = new ServiceHost(typeof(Service2), Service2_baseAddress); 
     Service2Host.Open(); 

     if (Service3Host != null) 
     { 
      Service3Host.Close(); 
     } 
     Service3Host = new ServiceHost(typeof(Service3), Service3_baseAddress); 
     Service3Host.Open(); 
    } 

    protected override void OnStop() 
    { 
     if (Service1Host != null) 
     { 
      Service1Host.Close(); 
      Service1Host= null; 
     } 
     if (Service2Host != null) 
     { 
      Service2Host.Close(); 
      Service2Host = null; 
     } 
     if (Service3Host != null) 
     { 
      Service3Host.Close(); 
      Service3Host = null; 
     } 
    } 
} 

我试过的是这里。我从app.config中删除了服务端点配置,并在代码中完成了它。我把所有的DLL放在一个文件夹中,并在代码中加载这些DLL。为了获得服务和接口,我在app.config中添加参数,如下所示,它将提供服务名称和接口名称以从加载的程序集中检索。它一切正常。但我有一个小问题,我在下面解释。

新App.config文件

<appSettings> 
<add key="Service1.dll" value="Service1"/> 
<add key="IService1.dll" value="IService1"/> 
<add key="Service2.dll" value="Service2"/> 
<add key="IService2.dll" value="IService2"/> 
<add key="Service3.dll" value="Service3"/> 
<add key="IService3.dll" value="IService3"/> 
</appSettings> 

新的Windows主机代码

public partial class Service : ServiceBase 
{ 
    public ServiceHost ServiceHost = null; 

    public WinService() 
    { 
     InitializeComponent(); 
    } 

    protected override void OnStart(string[] args) 
    { 
    string[] files = Directory.GetFiles(@"C:\Users\Desktop\WindowsHost\dlls", "*.dll"); 
     for (int i = 0; i < files.Length; i++) 
      files[i] = Path.GetFileName(files[i]); 

     foreach (var dllName in files) 
     { 
      string filePath = @"C:\Users\Desktop\WindowsHost\dlls\" + dllName; 
      Assembly assembly = Assembly.LoadFrom(filePath); 

      string serviceName = ConfigurationManager.AppSettings[dllName]; 
      string interfaceName = ConfigurationManager.AppSettings["I" + dllName]; 

      Type serviceToHost = assembly.GetType(serviceName); 
      var instance = Activator.CreateInstance(serviceToHost); 

      Type contract = service.GetInterface(interfaceName, true); 

      string address = dllName.Remove(dllName.LastIndexOf(".")); 

      Uri baseAddress = new Uri("http://localhost:9999/" + address); 

      if (ServiceHost != null) 
      { 
       ServiceHost.Close(); 
      } 
      ServiceHost = new ServiceHost(instance, baseAddress); 
      ServiceEndpoint sEP = ServiceHost.AddServiceEndpoint(contract, new WebHttpBinding(), ""); 
      WebHttpBehavior webHttpBeh = sEP.Behaviors.Find<WebHttpBehavior>(); 

      if (webHttpBeh != null) 
      { 
       webHttpBeh.AutomaticFormatSelectionEnabled = true; 
       webHttpBeh.DefaultOutgoingResponseFormat = WebMessageFormat.Json; 
       webHttpBeh.HelpEnabled = true; 
      } 
      else 
      { 
       WebHttpBehavior newWebHttpBeh = new WebHttpBehavior(); 
       newWebHttpBeh.AutomaticFormatSelectionEnabled = true; 
       newWebHttpBeh.DefaultOutgoingResponseFormat = WebMessageFormat.Json; 
       newWebHttpBeh.HelpEnabled = true; 
       sEP.Behaviors.Add(newWebHttpBeh); 
      } 

      ServiceHost.Open(); 
     } 
    } 

    protected override void OnStop() 
    { 
     if (ServiceHost != null) 
     { 
      ServiceHost.Close(); 
      ServiceHost = null; 
     } 
    } 
} 

我在做什么这里创建加载的DLL的实例,并收留了它作为Windows服务。

ServiceHost = new ServiceHost(instance, baseAddress); 

如果您的服务行为将InstanceContextMode设置为单个,这可以正常工作。否则它会给出错误。

错误: “服务无法启动System.InvalidOperationException:。为了使用ServiceHost的构造函数,需要一个服务实例之一,该服务的InstanceContextMode必须设置为InstanceContextMode.Single这可配置通过ServiceBehaviorAttribute。否则,请考虑使用带有Type参数的ServiceHost构造函数。“

我试图改变这样的

ServiceHost = new ServiceHost(typeof(serviceToHost), baseAddress); 

的代码,但它不工作。请帮忙。有没有其他方法可以实现这一点。

谢谢

+0

这是一个有趣的方法,但为什么不简单地为每个Web服务执行一个Windows服务?如果某项服务中出现异常,并且未发现并处理异常,则可能会导致Windows服务和所有托管的Web服务中断。 – Tim 2013-04-28 19:27:14

+0

谢谢蒂姆。我了解风险。但这是要求。此代码工作正常。唯一的问题是我没有为所有服务设置InstanceContextMode。 – Newbee 2013-04-29 08:45:54

+1

如果'输入serviceToHost = assembly.GetType(serviceName)'给你实现服务的类(即实现定义合同的接口的类),我会尝试'ServiceHost = new ServiceHost(serviceToHost,baseAddress);' - 你已经有了类型,不需要再次调用typeof,因为构造函数需要一个'Type'对象。 – Tim 2013-04-29 09:05:03

回答

1

这里是完整的工作代码。我已经根据需要添加了端点和服务行为。不是每个人都需要相同的配置,我猜。我把这个dll的名字添加到基地址的末尾。因此它根据dll名称为每个服务创建新地址。在app.config中定义的参数应该与dll的名称完全匹配。对于例如我使用三个DLL如下,然后我的app.config将如下面的代码所示。

  1. Service1.dll
  2. Service2.dll
  3. 服务3。dll的

的App.config

<appSettings> 
<add key="baseAddress" value="http://localhost:9999/"/> 
<add key="Service1.dll" value="namespace.Service1"/> 
<add key="Service1.dll" value="namespace.IService1"/> 
<add key="Service2.dll" value="namespace.Service2"/> 
<add key="Service2.dll" value="namespace.IService2"/> 
<add key="Service3.dll" value="namespace.Service3"/> 
<add key="Service3.dll" value="namespace.IService3"/> 
</appSettings> 

Windows安装程序代码

public ServiceHost[] serviceHost = null; 

    public MyService() 
    { 
     InitializeComponent(); 
    } 

    protected override void OnStart(string[] args) 
    {    
     try 
     { 
      //Get path for the executing assemblly 
      string exePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 
      //Path to the dlls to be hosted 
      string filePath = exePath + "\\DLLsToHost\\"; 
      //Retrieve only dll files from the folder 
      string[] files = Directory.GetFiles(@filePath, "*.dll"); 

      //get the dll file names 
      for (int i = 0; i < files.Length; i++) 
       files[i] = Path.GetFileName(files[i]); 

      //create an array of ServiceHost type 
      serviceHost = new ServiceHost[files.Length]; 
      //get the base address for the services from config file 
      string address = ConfigurationManager.AppSettings["baseAddress"]; 

      int j = 0; 

      foreach (var dllName in files) 
      { 
       string dllPath = filePath + dllName; 
       //Load the dll 
       Assembly assembly = Assembly.LoadFrom(@dllPath); 
       //Get the class name implementing the service 
       string serviceName = ConfigurationManager.AppSettings[dllName]; 
       //get the interface name implemented by the class 
       string interfaceName = ConfigurationManager.AppSettings["I" + dllName]; 

       if (serviceName == null || interfaceName == null) 
       { 
        //Log the error 
       } 
       else 
       { 
        //Get the class implementing the service 
        Type service = assembly.GetType(serviceName); 

        if (service != null) 
        { 
         //Get the interface implemented by the class 
         Type contract = service.GetInterface(interfaceName, true); 

         if (contract != null) 
         { 
          //Create a base address for the service 
          Uri baseAddress = new Uri(address + dllName.Remove(dllName.LastIndexOf("."))); 

          if (serviceHost[j] != null) 
          { 
           serviceHost[j].Close(); 
          } 

          serviceHost[j] = new CustomServiceHost(service, baseAddress); 
          //add the service endpoint and contract 
          ServiceEndpoint sEP = serviceHost[j].AddServiceEndpoint(contract, new WebHttpBinding(), ""); 
          WebHttpBehavior webHttpBeh = sEP.Behaviors.Find<WebHttpBehavior>(); 

          //Set the service and endpoint behaviours 
          if (webHttpBeh != null) 
          { 
           webHttpBeh.AutomaticFormatSelectionEnabled = true; 
           webHttpBeh.DefaultOutgoingResponseFormat = WebMessageFormat.Json; 
           webHttpBeh.HelpEnabled = true; 
           sEP.Behaviors.Add(new BehaviorAttribute());  //Add CORS support 
          } 
          else 
          { 
           WebHttpBehavior newWebHttpBeh = new WebHttpBehavior(); 
           newWebHttpBeh.AutomaticFormatSelectionEnabled = true; 
           newWebHttpBeh.DefaultOutgoingResponseFormat = WebMessageFormat.Json; 
           newWebHttpBeh.HelpEnabled = true; 
           sEP.Behaviors.Add(newWebHttpBeh); 
           sEP.Behaviors.Add(new BehaviorAttribute());  //Add CORS support 
          } 

          serviceHost[j].Open(); 
         } 
         else 
         { 
          //Log the error 
         } 
        } 
        else 
        { 
         //Log the error 
        } 
       } 
       j++; 
      } 
     } 
     catch(Exception ex) 
     { 
      //Throw the exception OR Log it 
     } 
    } 

    protected override void OnStop() 
    { 
     try 
     { 
      for (int k = 0; k <= serviceHost.Length - 1; k++) 
      { 
       if (serviceHost[k] != null) 
       { 
        serviceHost[k].Close(); 
        serviceHost[k] = null; 
       } 
      } 
     } 
     catch (Exception ex) 
     { 
      //Log 
     } 
    } 
} 

任何建议,以更新代码的欢迎。谢谢。

相关问题