引用 Windows 服务的 WCF 调用

本文关键字:WCF 调用 服务 Windows 引用 | 更新日期: 2023-09-27 17:55:54

我有一个长时间运行的服务,它从一个源获取数据,对其进行操作,将其存储在数据库中等。

我想向其他应用程序公开该服务上的一些方法。目前我们通过 .NET 远程处理执行此操作,但我想迁移到 WCF。

不幸的是,我连接到的终结点从来都不是我通过长时间运行的服务公开的终结点。下面是一个简单的例子:

    [ServiceContract]
    public interface ITestWcfService
    {
        [OperationContract]
        CounterResult GetCurrentValue();
    }
public class TestWcfService : ITestWcfService
    {
        private ITestWindowsService _service;
        public TestWcfService() { /*NOTE: For discoverability*/ }
        public TestWcfService(ITestWindowsService service)
        {
            _service = service;
        }
        public CounterResult GetCurrentValue()
        {
            return _service.GetCurrentValue();
        }
    }
public interface ITestWindowsService
    {
        CounterResult GetCurrentValue();
    }

然后我有了我的实际 Windows 服务,它通过 ServiceHost 类自托管 WCF 服务。

public partial class TestWindowsService : ServiceBase, ITestWindowsService
{
    private static ServiceHost _wcfService;
    public TestWindowsService()
    {
        InitializeComponent();
    }
    public void OnStart(string[] args)
    {
        //Create instance of WCF, passing in reference to this service instance
        TestWcfService wcf = new TestWcfService(this);
        _wcfService = new ServiceHost(wcf);
    }
        public CounterResult GetCurrentValue()
    {
        //Note: Some logic here
    }
}

现在,这或多或少有效,除了每次我调用TestWcfServiceClient()时,它使用默认构造函数并创建 Wcf 服务的新实例,而不使用 Windows 服务创建的实例。这意味着当我调用GetCurrentValue()时,我得到一个空引用,因为尚未设置_service成员。

我四处寻找解决方案,发现一些引用ServiceHostFactoryServiceHostIInstanceProvider,但每个似乎都非常非常复杂。

您能提供的任何想法将不胜感激。

编辑:这是我的服务模型信息

<system.serviceModel>
    <services>
      <service name="WcfService.TestWcfService">
        <host>
          <baseAddresses>
            <add baseAddress = "http://localhost:8733/Design_Time_Addresses/WcfService/TestWcfService/" />
          </baseAddresses>
        </host>
        <!-- Service Endpoints -->
        <!-- Unless fully qualified, address is relative to base address supplied above -->
        <endpoint address="" binding="basicHttpBinding" contract="WcfService.ITestWcfService">
          <!-- 
              Upon deployment, the following identity element should be removed or replaced to reflect the 
              identity under which the deployed service runs.  If removed, WCF will infer an appropriate identity 
              automatically.
          -->
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <!-- Metadata Endpoints -->
        <!-- The Metadata Exchange endpoint is used by the service to describe itself to clients. --> 
        <!-- This endpoint does not use a secure binding and should be secured or removed before deployment -->
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, 
          set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="True" httpsGetEnabled="True"/>
          <!-- To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

引用 Windows 服务的 WCF 调用

WCF 非常可扩展,但为了提供这种可扩展性,它也非常复杂。在我看来,如果你想使用这个实现,那就不仅仅是应该的。您必须创建一个更改服务工厂的行为,以便可以使用自定义构造函数而不是空构造函数。

但是,我认为可能还有另一种选择。由于服务和 WCF 共享相同的应用程序域,因此它们可以通过静态对象共享值。

我会将携带要公开的值的逻辑移动到非基础程序集(如果尚未移动),并且服务和 WCF 服务都将看到相同的实例,因此您无需更改所有 WCF 管道。

您正在尝试将方形钉子放在圆孔中。

WCF 显式设计为启用独立于其承载方式的调用业务逻辑。您正在尝试维护旧的远程处理设计,该设计在主机层(在本例中为 Window 的服务)中具有业务逻辑。因此,您要求以 WCF 中可能的方式将信息传递回主机,但这种方式很丑陋,并且通常出于许多充分的原因而避免。

如果需要干净的 WCF 设计,则需要创建抽象层并将业务逻辑移出现有的 TestWindowsService 类。然后,Windows 服务创建 WCF 服务宿主,WCF 宿主创建 WCF 服务,并且 WCF 服务不依赖于承载它的类。

说了这么多...

要实际回答您的问题:

从概念上讲,您编写的代码应该可以工作。将对象实例传递到 ServiceHost 构造函数中是迄今为止避免编写自定义 IInstanceProvider 的复杂性的最简单方法。要检查的几件事:

  1. 将服务实例传递到 ServiceHost 构造函数时,需要将其标记为单一实例。

    [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]

  2. 确保初始化确实按照您认为的方式工作:

    1. 在你的TestService(ITestWindowsService)构造函数上放置一个守卫,如果服务为空,则抛出。
    2. 您对 TestWcfService() 的默认构造函数有一条注释,上面写着"for discoverability"。我会删除该构造函数(至少在您进行故障排除时)。一段代码可能使用该构造函数来承载您不需要的服务实例。这可能会导致您的应用开始引发异常或编译错误,从而指出您的真正问题。

如果仍有问题:app.config 的"服务模型"部分是什么样的?