如何为没有SVC文件的自托管WCF服务指定ServiceHostFactory

7
我的应用程序作为Windows服务运行。它使用以下辅助方法动态创建WCF服务:
    public static void StartWebService(string webServiceName, Type serviceContractType, Type serviceImplementationType)
    {
        if (string.IsNullOrEmpty(webServiceName)) return;

        var baseAddress = GetWebServiceAddress(webServiceName);
        var baseUri = new Uri(baseAddress);

        lock (RunningWebServices)
        {
            if (RunningWebServices.ContainsKey(webServiceName))
                return;

            var webServiceHost = new ServiceHost(serviceImplementationType, baseUri);

            var serviceBehaviour = new ServiceMetadataBehavior() { HttpGetEnabled = true };
            webServiceHost.Description.Behaviors.Add(serviceBehaviour);
            webServiceHost.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");

            var httpBinding = new BasicHttpBinding();
            webServiceHost.AddServiceEndpoint(serviceContractType, httpBinding, baseAddress);
            webServiceHost.Open();

            RunningWebServices.Add(webServiceName, webServiceHost);
        }
    }

这些服务没有 .SVC 文件。同时请明确,这些是自托管服务,而不是在 IIS 下运行 - 它们在 WAS (Windows Activation Services) 下运行。
现在我想控制实现类的实例化方式。听起来可以使用 ServiceHostFactory 来实现。然而,我找到的所有关于此的文章都说指定要使用的工厂的方法是将类型名称放在 .SVC 文件的 @ServiceHost 指令中。我没有这个文件!
是否有任何方法可以在 WAS 下运行的 WCF 服务中使用自己的 ServiceHost 工厂? 回答后更新 以下是我的新辅助方法,包含 Carlos 的解决方案。
    public static void StartWebService(string webServiceName, Type serviceContractType, Type serviceImplementationType)
    {
        if (string.IsNullOrEmpty(webServiceName)) return;

        var baseAddress = GetWebServiceAddress(webServiceName);
        var baseUri = new Uri(baseAddress);

        lock (RunningWebServices)
        {
            if (RunningWebServices.ContainsKey(webServiceName))
                return;

            var webServiceHost = new ServiceHost(serviceImplementationType, baseUri);

            webServiceHost.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true });
            webServiceHost.Description.Behaviors.Add(new CustomWebServiceBehavior());

            webServiceHost.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
            webServiceHost.AddServiceEndpoint(serviceContractType, new BasicHttpBinding(), baseAddress);

            webServiceHost.Open();

            RunningWebServices.Add(webServiceName, webServiceHost);
        }
    }

internal class CustomWebServiceBehavior : IServiceBehavior, IInstanceProvider
{
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHost)
    {
        foreach (ChannelDispatcher cd in serviceHost.ChannelDispatchers)
        {
            foreach (EndpointDispatcher ed in cd.Endpoints)
            {
                ed.DispatchRuntime.InstanceProvider = this;
            }
        }
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }

    public object GetInstance(InstanceContext instanceContext, Message message)
    {
        return this.GetInstance(instanceContext);
    }

    public object GetInstance(InstanceContext instanceContext)
    {
        return // this is where I use my factory;
    }

    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
    }
}
1个回答

4
您不需要使用服务主机工厂来控制服务实现类的实例化方式 - 您需要将 IInstanceProvider 添加到您的分派运行时中,这可以通过使用自定义服务行为来实现。博客文章 http://blogs.msdn.com/b/carlosfigueira/archive/2011/05/31/wcf-extensibility-iinstanceprovider.aspx 中有更多详细信息,介绍了如何实现此功能。例如,下面的 SSCCE 显示了一个示例,该示例展示了如何使用实例提供程序(对于没有默认构造函数的服务,因此在 WCF 中不能直接使用而需要实例提供程序)。
public class StackOverflow_29825519
{
    [ServiceContract]
    public interface ITest
    {
        [OperationContract]
        string WhoAmI();
    }
    public class Service : ITest
    {
        string name;

        public Service(string name)
        {
            this.name = name;
        }

        public string WhoAmI()
        {
            return this.name;
        }
    }
    class MyBehavior : IServiceBehavior, IInstanceProvider
    {
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcher cd in serviceHostBase.ChannelDispatchers)
            {
                foreach (EndpointDispatcher ed in cd.Endpoints)
                {
                    ed.DispatchRuntime.InstanceProvider = this;
                }
            }
        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }

        public object GetInstance(InstanceContext instanceContext, Message message)
        {
            return this.GetInstance(instanceContext);
        }

        public object GetInstance(InstanceContext instanceContext)
        {
            return new Service("John Doe");
        }

        public void ReleaseInstance(InstanceContext instanceContext, object instance)
        {
        }
    }
    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "");
        host.Description.Behaviors.Add(new MyBehavior());
        host.Open();
        Console.WriteLine("Host opened");

        ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
        ITest proxy = factory.CreateChannel();
        Console.WriteLine("WhoAmI: {0}", proxy.WhoAmI());

        ((IClientChannel)proxy).Close();
        factory.Close();

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}

谢谢!我读了你的文章并创建了自己的IInstanceProvider。我试图在添加了BasicHttpBinding的服务端点之后,在调用webServiceHost.Open()之前应用它。然而,我的服务(由上面的辅助方法创建)在serviceHost.ChannelDispatchers中没有任何项目可以附加IInstanceProvider。你能指点我正确的方向吗……我需要编程创建ChannelDispatchers吗? - Laurence
看一下我上面添加的例子。它应该可以正常工作。从那里你可以将其与你的代码进行比较,找出问题所在。 - carlosfigueira
非常感谢 - 我现在已经让它工作了。请查看更新的问题以获取新代码。 - Laurence

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接