【发布时间】:2013-04-28 15:35:41
【问题描述】:
我有 3 个 Restful 服务,我想将它们作为 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);
但它不起作用。请帮忙。有没有其他方法可以实现这一点。
谢谢
【问题讨论】:
-
这是一种有趣的方法,但为什么不简单地为每个 Web 服务做一个 Windows 服务呢?如果您的一项服务发生故障并且未捕获和处理异常怎么办 - 您可能会关闭 Windows 服务和所有托管的 Web 服务。
-
谢谢蒂姆。我理解风险。但这是要求。这段代码工作正常。唯一的问题是我没有将所有服务的 InstanceContextMode 设置为 single。
-
如果
Type serviceToHost = assembly.GetType(serviceName)为您提供实现服务的类(即实现定义合同的接口的类),我会尝试ServiceHost = new ServiceHost(serviceToHost, baseAddress);- 您已经有了类型,无需调用 typeof再次,因为构造函数需要一个Type对象。 -
蒂姆,你就是那个男人。它工作正常。不过,我不得不对代码进行一些更改。必须创建一个 ServiceHost 数组。并使用它来托管所有服务。让我们看看这段代码能持续多久。 :) 再次感谢
-
我很高兴你得到它的工作 - 你可以考虑发布你的最终解决方案作为一个答案(你可以在一段时间后自动接受它),以供可能处于相同情况的未来开发人员使用。此外,由于您有一个单点故障(一切都在一个 Windows 服务中),我建议您考虑在您的服务中实现
IErrorHandler- 这样您就可以捕获任何原本无法处理的错误并降低风险把一切都搞砸了。
标签: c# asp.net wcf rest .net-4.0