How to specify ServiceHostFactory for self-hosted

2019-06-27 04:55发布

问题:

My application runs as a Windows service. It dynamically creates WCF services using the following helper method:

    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);
        }
    }

There is no .SVC file for these services. Also be clear that these are self-hosted services, not running under IIS - they are running under WAS (Windows Activation Services).

Now I want to control how the implementation class is instantiated. It sounds like this can be done with a ServiceHostFactory. However all the articles about this I have found say that the way to specify what factory to used is to put the type name in the @ServiceHost directive of the .SVC file. I don't have this file!

Is there any way to use my own ServiceHost factory for WCF services with no SVC file running under WAS?

Update following answer

Here is my new helper method incorporating Carlos' solution

    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:

You don't need a service host factory to control how the service implementation class is instantiated - what you need is to add an IInstanceProvider to your dispatch runtime, by using a custom service behavior. The blog post at http://blogs.msdn.com/b/carlosfigueira/archive/2011/05/31/wcf-extensibility-iinstanceprovider.aspx has a lot more details on how this can be achieved. For example, the SSCCE below shows how an example of an instance provider being used (for a service which does not have a default constructor, so it cannot be used directly, without an instance provider, in 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();
    }
}