Export (0) Print
Expand All

Using Instance Provider and ServiceHostFactory to Construct the Service

.NET Framework 4

Applies to: Windows Communication Foundation

Published: June 2011

Author: Alex Culp

While the approach shown in the previous section works well, it does require that you do not put exception logic, performance counters, or validation logic directly in the service implementation. Instead, you must create a second class (In the previous example, this is the Manager class.). You then have to route the code from the service implementation to that class. This is shown in the following code.

public void DoWork()
{
     _exampleManager.DoWork();
}

An improvement on the previous example would allow you to inject behaviors into the service without an unnecessary abstraction layer. The following code shows this improvement.

public void DoWork()
{
     //do some work
}

Another improvement would be to reduce the number of constructors from two to just one "greedy" constructor (that is, the constructor that has all of the parameters).

Finally, not everyone wants to use a configuration file to configure Unity. Unity configuration is somewhat tedious because there is no management tool, and any error breaks the DI. Therefore, it is often preferable to use code to register dependencies.

The following approach creates a custom instance provider that is combined with a service host factory, which registers the instance provider. This approach removes the extra layer of abstraction, the extra constructor, and the limitation of using configuration files.

NoteNote

You cannot use a custom ServiceHostFactory if the service's InstanceContextMode property is set to Single. However, you can mimic the behavior of the InstanceContextMode Single with Unity's ContainerControlledLifetimeManager class.

To write a custom ServiceHostFactory, follow these steps:

  1. Create the instance provider.

  2. Create the service behavior.

  3. Add the service behavior to the service.

  4. Create a custom ServiceHostFactory.

  5. Create a concrete implementation of the DependencyInjectionServiceHostFactory class.

  6. Configure the SVC file.

The first step is to create a custom instance provider that uses Unity to resolve the instance to the service, rather than allowing WCF to create the instance of the service itself. To create a custom instance provider, create a class that implements IInstanceProvider. This is shown in the following code.

Visual C# Example Instance Provider

/// <summary>
/// A custom instance provider that uses the ServiceLocator from MS Pattern and Practices to resolve service types.
/// </summary>
/// <remarks>
public class DependencyInjectionInstanceProvider : IInstanceProvider
{
    private readonly Type _serviceType;

    /// <summary>
    /// Initializes a new instance of the <see cref="DependencyInjectionInstanceProvider"/> class.
    /// </summary>
    /// <param name="serviceType">Type of the service.</param>
    public DependencyInjectionInstanceProvider(Type serviceType)
    {
        _serviceType = serviceType;
    }

    /// <summary>
    /// Returns a service object given the specified <see cref="T:System.ServiceModel.InstanceContext"/> object.
    /// </summary>
    /// <param name="instanceContext">The current <see cref="T:System.ServiceModel.InstanceContext"/> object.</param>
    /// <returns>A user-defined service object.</returns>
    public object GetInstance(InstanceContext instanceContext)
    {
        return GetInstance(instanceContext, null);
    }

    /// <summary>
    /// Returns a service object given the specified <see cref="T:System.ServiceModel.InstanceContext"/> object.
    /// </summary>
    /// <param name="instanceContext">The current <see cref="T:System.ServiceModel.InstanceContext"/> object.</param>
    /// <param name="message">The message that triggered the creation of a service object.</param>
    /// <returns>The service object.</returns>
    public object GetInstance(InstanceContext instanceContext, Message message)
    {
        return DependencyFactory.Resolve(_serviceType);  
    }

    /// <summary>
    /// Called when an <see cref="T:System.ServiceModel.InstanceContext"/> object recycles a service object.
    /// </summary>
    /// <param name="instanceContext">The service's instance context.</param>
    /// <param name="instance">The service object to be recycled.</param>
    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
    }
}

Visual Basic Example Instance Provider

''' <summary>
''' A custom instance provider that uses the ServiceLocator from MS Pattern and Practices to resolve service types.
''' </summary>
''' <remarks>
Public Class DependencyInjectionInstanceProvider
     Implements IInstanceProvider
     Private ReadOnly _serviceType As Type

     ''' <summary>
     ''' Initializes a new instance of the <see cref="DependencyInjectionInstanceProvider"/> class.
     ''' </summary>
     ''' <param name="serviceType">Type of the service.</param>
     Public Sub New(serviceType As Type)
          _serviceType = serviceType
     End Sub

     ''' <summary>
     ''' Returns a service object given the specified <see cref="T:System.ServiceModel.InstanceContext"/> object.
     ''' </summary>
     ''' <param name="instanceContext">The current <see cref="T:System.ServiceModel.InstanceContext"/> object.</param>
     ''' <returns>A user-defined service object.</returns>
     Public Function GetInstance(instanceContext As InstanceContext) As Object
          Return GetInstance(instanceContext, Nothing)
     End Function

     ''' <summary>
     ''' Returns a service object given the specified <see cref="T:System.ServiceModel.InstanceContext"/> object.
     ''' </summary>
     ''' <param name="instanceContext">The current <see cref="T:System.ServiceModel.InstanceContext"/> object.</param>
     ''' <param name="message">The message that triggered the creation of a service object.</param>
     ''' <returns>The service object.</returns>
     Public Function GetInstance(instanceContext As InstanceContext, message As Message) As Object
          Return DependencyFactory.Resolve(_serviceType)
     End Function

     ''' <summary>
     ''' Called when an <see cref="T:System.ServiceModel.InstanceContext"/> object recycles a service object.
     ''' </summary>
     ''' <param name="instanceContext">The service's instance context.</param>
     ''' <param name="instance">The service object to be recycled.</param>
     Public Sub ReleaseInstance(instanceContext As InstanceContext, instance As Object)
     End Sub
End Class

The next step is to create a service behavior that registers the instance provider. This is shown in the following code.

Visual C# Example Service Behavior

/// <summary>
/// A custom service behavior which will register the dependency injection instance provider.
/// </summary>
public class DependencyInjectionServiceBehavior : IServiceBehavior
{
    #region IServiceBehavior Members

    /// <summary>
    /// Provides the ability to pass custom data to binding elements to support the contract implementation.
    /// </summary>
    /// <param name="serviceDescription">The service description of the service.</param>
    /// <param name="serviceHostBase">The host of the service.</param>
    /// <param name="endpoints">The service endpoints.</param>
    /// <param name="bindingParameters">Custom objects to which binding elements have access.</param>
    public void AddBindingParameters(ServiceDescription serviceDescription, 
        System.ServiceModel.ServiceHostBase serviceHostBase, 
        System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, 
        System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    { }

    /// <summary>
    /// Provides the ability to change run-time property values or insert custom extension objects such as error handlers, message or parameter interceptors, security extensions, and other custom extension objects.
    /// </summary>
    /// <param name="serviceDescription">The service description.</param>
    /// <param name="serviceHostBase">The host that is currently being built.</param>
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
    {
        foreach (var cdb in serviceHostBase.ChannelDispatchers)
        {
            var cd = cdb as ChannelDispatcher;

            if (cd != null)
            {
                foreach (var ed in cd.Endpoints)
                {
                    ed.DispatchRuntime.InstanceProvider = new DependencyInjectionInstanceProvider(serviceDescription.ServiceType);
                }
            }
        }
    }

    /// <summary>
    /// Provides the ability to inspect the service host and the service description to confirm that the service can run successfully.
    /// </summary>
    /// <param name="serviceDescription">The service description.</param>
    /// <param name="serviceHostBase">The service host that is currently being constructed.</param>
    public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
    { }

    #endregion
}

Visual Basic Example Service Behavior

''' <summary>
''' A custom service behavior which will register the depenency injection instance provider.
''' </summary>
Public Class DependencyInjectionServiceBehavior
Implements IServiceBehavior
#Region "IServiceBehavior Members"

''' <summary>
''' Provides the ability to pass custom data to binding elements to support the contract implementation.
''' </summary>
''' <param name="serviceDescription">The service description of the service.</param>
''' <param name="serviceHostBase">The host of the service.</param>
''' <param name="endpoints">The service endpoints.</param>
''' <param name="bindingParameters">Custom objects to which binding elements have access.</param>
Public Sub AddBindingParameters(serviceDescription As ServiceDescription, serviceHostBase As System.ServiceModel.ServiceHostBase, endpoints As System.Collections.ObjectModel.Collection(Of ServiceEndpoint), bindingParameters As System.ServiceModel.Channels.BindingParameterCollection)
End Sub

''' <summary>
''' Provides the ability to change run-time property values or insert custom extension objects such as error handlers, message or parameter interceptors, security extensions, and other custom extension objects.
''' </summary>
''' <param name="serviceDescription">The service description.</param>
''' <param name="serviceHostBase">The host that is currently being built.</param>
Public Sub ApplyDispatchBehavior(serviceDescription As ServiceDescription, serviceHostBase As System.ServiceModel.ServiceHostBase)
For Each cdb As var In serviceHostBase.ChannelDispatchers
Dim cd = TryCast(cdb, ChannelDispatcher)

If cd IsNot Nothing Then
For Each ed As var In cd.Endpoints
ed.DispatchRuntime.InstanceProvider = New DependencyInjectionInstanceProvider(serviceDescription.ServiceType)
Next
End If
Next
End Sub

''' <summary>
''' Provides the ability to inspect the service host and the service description to confirm that the service can run successfully.
''' </summary>
''' <param name="serviceDescription">The service description.</param>
''' <param name="serviceHostBase">The service host that is currently being constructed.</param>
Public Sub Validate(serviceDescription As ServiceDescription, serviceHostBase As System.ServiceModel.ServiceHostBase)
End Sub

#End Region
End Class

To register the service behavior with the service, create a custom ServiceHost. This is shown in the following code.

Visual C# Example ServiceHost

/// <summary>
/// This service host is used to set up the service behavior that replaces the instance provider to use dependency injection.
/// </summary>
public class DependencyInjectionServiceHost : ServiceHost
{
    /// <summary>
    /// Initializes a new instance of the <see cref="DependencyInjectionServiceHost"/> class.
    /// </summary>
    /// <param name="serviceType">Type of the service.</param>
    /// <param name="baseAddresses">The base addresses.</param>
    public DependencyInjectionServiceHost(Type serviceType, Uri[] baseAddresses)
        : base(serviceType, baseAddresses)
    { }

    /// <summary>
    /// Opens the channel dispatchers.
    /// </summary>
    /// <param name="timeout">The <see cref="T:System.Timespan"/> that specifies how long the on-open operation has to complete before timing out.</param>
    protected override void OnOpen(TimeSpan timeout)
    {
        Description.Behaviors.Add(new DependencyInjectionServiceBehavior());
        base.OnOpen(timeout);
    }
}

Visual Basic Example ServiceHost

''' <summary>
''' This service host is used to set up the service behavior that replaces the instance provider to use dependency injection.
''' </summary>
Public Class DependencyInjectionServiceHost
Inherits ServiceHost
''' <summary>
''' Initializes a new instance of the <see cref="DependencyInjectionServiceHost"/> class.
''' </summary>
''' <param name="serviceType">Type of the service.</param>
''' <param name="baseAddresses">The base addresses.</param>
Public Sub New(serviceType As Type, baseAddresses As Uri())
MyBase.New(serviceType, baseAddresses)
End Sub

''' <summary>
''' Opens the channel dispatchers.
''' </summary>
''' <param name="timeout">The <see cref="T:System.Timespan"/> that specifies how long the on-open operation has to complete before timing out.</param>
Protected Overrides Sub OnOpen(timeout As TimeSpan)
Description.Behaviors.Add(New DependencyInjectionServiceBehavior())
MyBase.OnOpen(timeout)
End Sub
End Class

The next step creates the custom ServiceHostFactory class that creates a new instance of the DepenencyInjectionServiceHost class, which in turn provides an abstract method for registering any dependencies. This is shown in the following code.

Visual C# Example Dependency Injection ServiceHostFactory

/// <summary>
/// Produces <see cref="DependencyInjectionServiceHost"/>s.
/// </summary>
public abstract class DependencyInjectionServiceHostFactory : ServiceHostFactory
{
    /// <summary>
    /// Creates a <see cref="DependencyInjectionServiceHost"/> for a specified type of service with a specific base address. 
    /// </summary>
    /// <returns>
    /// A <see cref="DependencyInjectionServiceHost"/> for the type of service specified with a specific base address.
    /// </returns>
    /// <param name="serviceType">
    /// Specifies the type of service to host. 
    /// </param>
    /// <param name="baseAddresses">
    /// The <see cref="T:System.Array"/> of type <see cref="T:System.Uri"/> that contains the base addresses for the service hosted.
    /// </param>
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        //Register the service as a type so it can be found from the instance provider
        DependencyFactory.Container.RegisterType(serviceType);

        RegisterDependencies();
        return new DependencyInjectionServiceHost(serviceType, baseAddresses);
    }

    /// <summary>
    /// Initialization logic that any derived type would use to set up the ServiceLocator provider.  Look to UnityServiceHostFactory to see how this is done, if implementing for 
    /// another IoC engine.
    /// </summary>
    protected abstract void RegisterDependencies();
}

Visual Basic Example Dependency Injection ServiceHostFactory


''' <summary>
''' Produces <see cref="DependencyInjectionServiceHost"/>s.
''' </summary>
Public MustInherit Class DependencyInjectionServiceHostFactory
     Inherits ServiceHostFactory
     ''' <summary>
     ''' Creates a <see cref="DependencyInjectionServiceHost"/> for a specified type of service with a specific base address. 
     ''' </summary>
     ''' <returns>
     ''' A <see cref="DependencyInjectionServiceHost"/> for the type of service specified with a specific base address.
     ''' </returns>
     ''' <param name="serviceType">
     ''' Specifies the type of service to host. 
     ''' </param>
     ''' <param name="baseAddresses">
     ''' The <see cref="T:System.Array"/> of type <see cref="T:System.Uri"/> that contains the base addresses for the service hosted.
     ''' </param>
     Protected Overrides Function CreateServiceHost(serviceType As Type, baseAddresses As Uri()) As ServiceHost
          'Register the service as a type so it can be found from the instance provider
          DependencyFactory.Container.RegisterType(serviceType)

          RegisterDependencies()
          Return New DependencyInjectionServiceHost(serviceType, baseAddresses)
     End Function

     ''' <summary>
     ''' Initialization logic that any derived type would use to set up the ServiceLocator provider.  Look to UnityServiceHostFactory to see how this is done, if implementing for 
     ''' another IoC engine.
     ''' </summary>
     Protected MustOverride Sub RegisterDependencies()
End Class

This step creates a concrete implementation of the DependencyInjectionServiceHostFactory class for your specific service. The only thing you have to do is implement the abstract method RegisterDependencies, which is what you will use to register your dependencies with Unity. The following code shows an example of a service for a Human Resources (HR) department.

Visual C# Example Custom ServiceHostFactory

/// <summary>
/// A custom service host factory that will register the dependencies with unity before the service is created.
/// </summary>
public class ExampleServiceHostFactory:DependencyInjectionServiceHostFactory
{
    protected override void RegisterDependencies()
    {
        DependencyFactory.Container.RegisterType(typeof(IExampleRepository),typeof(ExampleRepository),new ContainerControlledLifetimeManager());
    }
}

Visual Basic Example Custom ServiceHostFactory

''' <summary>
''' A custom service host factory that will register the dependencies with unity before the service is created.
''' </summary>
Public Class ExampleServiceHostFactory
Inherits DependencyInjectionServiceHostFactory
Protected Overrides Sub RegisterDependencies()
DependencyFactory.Container.RegisterType(GetType(IExampleRepository), GetType(ExampleRepository), New ContainerControlledLifetimeManager())
End Sub
End Class

The last step configures the SVC file to use the custom ServiceHostFactory that was created in step 5. This is shown in the following code.

<%@ ServiceHost Language="C#" Debug="true" Service="ExampleService" 
CodeBehind="ExampleService.cs"  Factory="ExampleServiceHostFactory"%>

Obviously, creating a custom ServiceHostFactory requires a fair amount of code. However, only the code in steps five and six must be written more than once, after you create the reusable classes. Each new service needs only the concrete implementation of the DependencyInjectionServiceHostFactory, and the code that configures the SVC file.

A custom service host factory in an SVC file has two advantages over configuration files. First, because Unity has no tool for updating configuration files, registering your dependencies in code avoids many configuration management problems. The second advantage is that you will know at compile time when one of your dependencies fails. If you use a configuration file, you will not know until run time.

Show:
© 2014 Microsoft