WCF ServiceBehavior Attribute and Configuration

Nov 25, 2011 at 3:42 PM

I'm trying to get Unity to work in an WCF application, and I need help with a couple things.

1. Given the following class, what would the configuration in a .config file look like to have the DataController property set to a instance of a "DataController" object?

Public Class StakeholderBP
    Implements IStakeholderBP

    Public Property DataController As IDataController

    'Functions calling DataController methods (eg. DataController.DoSomething)
End Class

2. There are several examples available (eg. http://blogs.microsoft.co.il/blogs/gadib/archive/2010/11/30/wcf-and-unity-2-0.aspx) on how to create a ServiceBehavior with a custom ServiceHost and ServiceHostFactory.  I however would like to just be able add an Attribute to a service and not have to create a custom host and factory.  Does anybody have an example of such an attribute?

Nov 25, 2011 at 4:42 PM

1.

Try this configuration:

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
  </configSections>

  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <namespace name="MyNamespace" />
    <assembly name="MyAssembly" />

    <container>
      <register type="StakeholderBP">
        <property dependencyType="ConcreteDataController" name="DataController" />
      </register>
    </container>
    
  </unity>
</configuration>

2.

For an example of how to create a ServiceBehavior that is configured via an attribute look at the Exception Handling Block Source.  
Specifically the ExceptionHandling.WCF.2010 project has the Exception Shielding IServiceBehavior implementation.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com

Nov 25, 2011 at 5:12 PM

Thanks Randy.

I should clarify item #2.  I know how to create a ServiceBehavior that is applied using an Attribute. 

I know i'm going to need a custom class that implements IInstanceProvider, as well as another class implements IServiceBehavior which sets the InstanceProvider provider property of each endpoint.  What I don't know is what additional steps are necessary, like how I setup the UnityContainer and where.

Nov 25, 2011 at 6:05 PM

If I'm understanding you correctly, I think you will need to implement ServiceHost and ServiceHostFactory.  If you host inside IIS you could use IIS specific hooks (e.g. Application_Start) to setup the Unity container.  I know you've read some blogs but I think this one is good (and provides a bunch of other links): Avoiding Multiple IOC Container Instances in WCF.

 

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com

Nov 25, 2011 at 7:16 PM
Edited Nov 25, 2011 at 7:48 PM

Thanks again Randy, I've put something together however it is not working.  All I get is a NullREferenceException when I  access the DataController property on the StakeholderBP class. Is something missing?

Public Class UnityServiceBehaviorAttribute
    Inherits Attribute
    Implements IServiceBehavior

    Private _Container As UnityContainer

    Public Sub New()
        Dim container As New UnityContainer()
        Dim unityConfig As UnityConfigurationSection = TryCast(ConfigurationManager.GetSection("unity"), UnityConfigurationSection)
        unityConfig.Configure(container)
        _Container = container
    End Sub

    Public Sub AddBindingParameters(serviceDescription As ServiceDescription, serviceHostBase As ServiceHostBase, endpoints As Collection(Of ServiceEndpoint),
                                    bindingParameters As BindingParameterCollection) Implements IServiceBehavior.AddBindingParameters
       
    End Sub

    Public Sub ApplyDispatchBehavior(serviceDescription As ServiceDescription, serviceHostBase As ServiceHostBase) Implements IServiceBehavior.ApplyDispatchBehavior
        Dim instanceProvider As New UnityInstanceProvider(serviceDescription.ServiceType, _Container)
        For Each channelDispatcherBase As ChannelDispatcherBase In serviceHostBase.ChannelDispatchers
            Dim channelDispatcher As ChannelDispatcher = TryCast(channelDispatcherBase, ChannelDispatcher)
            If channelDispatcher IsNot Nothing Then
                For Each endpoint As EndpointDispatcher In channelDispatcher.Endpoints
                    endpoint.DispatchRuntime.InstanceProvider = instanceProvider
                Next
            End If
        Next
    End Sub

    Public Sub Validate(serviceDescription As ServiceDescription, serviceHostBase As ServiceHostBase) Implements IServiceBehavior.Validate

    End Sub
End Class

Public Class UnityInstanceProvider
    Implements IInstanceProvider

    Private _ServiceType As Type
    Private _Container As IUnityContainer
    Public Sub New(ByVal serviceType As Type, ByVal container As IUnityContainer)
        _ServiceType = serviceType
        _Container = container
    End Sub

    Public Function GetInstance(instanceContext As InstanceContext) As Object Implements IInstanceProvider.GetInstance
        Return Me.GetInstance(instanceContext, Nothing)
    End Function

    Public Function GetInstance(instanceContext As InstanceContext, message As Message) As Object Implements IInstanceProvider.GetInstance
        Return _Container.Resolve(_ServiceType)
    End Function

    Public Sub ReleaseInstance(instanceContext As InstanceContext, instance As Object) Implements IInstanceProvider.ReleaseInstance
    End Sub
End Class

 Here is my configuration:

<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <alias alias="dc" type="POC.Service.DataAccess.DataController, POC.Service.DataAccess"/>
    <alias alias="sBP" type="POC.Service.Business.Process.StakeholderBP, POC.Service.Business.Process"/>
    <container>
      <register type="sBP">
        <property name="DataController" dependencyType="dc" />
      </register>
    </container>
  </unity>

 Service:

<UnityServiceBehavior()>
Public Class POCService
    Implements IPOCService

    'Service Implementation
 End Class

 

Nov 27, 2011 at 8:41 PM
Edited Nov 27, 2011 at 8:51 PM
I have implemented IoC inside WCF, but I have an extra EndpointBehavior.
In my example here, I have an custom host factory ( UnityServiceHostFactory : ServiceHostFactory ) and a ServiceHost<T> : ServiceHost as well, 
but the custom host factory ( unmongst other things ) just adds the UnityServiceBehavior to the servicehost's behavior collection.
In summary
  1. UnityServiceHostFactory builds the container ( from configuration files )
  2. UnityServiceHostFactory adds UnityServiceBehavior to each service's behaviors
  3. UnityServiceBehavior adds the UnityEndpointBehavior to each contract endpoint ( ignores Mex and Disco endpoints ) in the ApplyDispatchBehavior method
  4. UnityEndpointBehavior assigns a new UnityInstanceProvider to the InstanceProvider of the endpoint dispatcher, supplying the container and type information in the constructor.
To check if your instance provider is being used, try changing the service class to have a non-default constructor... yours should work, the default one fails. 
My example below each service has a wrapper around OperationContext and a LogEngine supplied.
I also prefer using constructor parameters instead of property injection as I find it safer during unit testing.
 
public class UnityServiceBehavior : IServiceBehavior
{
    private readonly IUnityContainer container;

    public UnityServiceBehavior(IUnityContainer container)
    {
        this.container = container;
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
    {
        if (serviceDescription.Endpoints.Where( ServiceModelExtensions.ServiceEndpointType.ContractEndpoint).Any(endpoint => endpoint.Behaviors.Find<UnityEndpointBehavior>() != null))
            return;

        serviceDescription.Endpoints
            .Where(ServiceModelExtensions.ServiceEndpointType.ContractEndpoint)
            .ForEach(endpoint =>
                         {
                             endpoint.Behaviors.FindOrCreate(() => new UnityEndpointBehavior(this.container));
                         }
            );
    }
}

public class UnityEndpointBehavior : IEndpointBehavior
{
    private readonly IUnityContainer container;

    public UnityEndpointBehavior(IUnityContainer container)
    {
        this.container = container;
    }

    void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
    {
        if ( endpoint.Contract.ContractType == typeof( IMetadataExchange ) )
            return;

        endpointDispatcher.DispatchRuntime.InstanceProvider = new UnityInstanceProvider(endpoint.Contract.ContractType, this.container );
    }
}

public class UnityInstanceProvider : IInstanceProvider
{
    private readonly IUnityContainer container;
    private readonly Type type;
        
    public UnityInstanceProvider( Type type, IUnityContainer serviceContainer )
    {
        this.type = type;
        this.container = serviceContainer;
    }

    #region IInstanceProvider Members
    object IInstanceProvider.GetInstance(InstanceContext instanceContext, System.ServiceModel.Channels.Message message)
    {
        return ( (IInstanceProvider) this ).GetInstance( instanceContext );
    }

    object IInstanceProvider.GetInstance(InstanceContext instanceContext)
    {
        return container.Resolve( this.type );
    }

    void IInstanceProvider.ReleaseInstance(InstanceContext instanceContext, object instance)
    {
        if ( instance is IDisposable )
        {
            (instance as IDisposable).Dispose();
        }
    }

    #endregion
}
public class MyService : IMyService
{
    private IUnityContainer container;
    private IOperationContext CurrentContext;
    private ILogEngine LogEngine;

    public MyService(IUnityContainer container, IOperationContext context, ILogEngine logEngine)
    {
        this.CurrentContext = context;
        this.LogEngine = logEngine;
        this.container = container;
    }
}
Nov 28, 2011 at 2:17 PM

Looks like I got it working. It would appear my service implementation needed to be updated.

Here is what I had for my service implementation:

<UnityServiceBehavior()>
Public Class POCService
    Implements IPOCService

    'Service Implementation
    Public Function GetStakeholder(stakeholderGuid As System.Guid) As Stakeholder Implements Contracts.IPOCService.GetStakeholder
        Dim bp As New StakeholderBP
        Return bp.GetStakeholder(stakeholderGuid)
    End Function

End Class

I changed it to this and added another piece to my unity config.

<UnityServiceBehavior()>
Public Class POCService
    Implements IPOCService

    Public Property BP As StakeholderBP

    'Service Implementation
    Public Function GetStakeholder(stakeholderGuid As System.Guid) As Stakeholder Implements Contracts.IPOCService.GetStakeholder
        Return bp.GetStakeholder(stakeholderGuid)
    End Function
End Class
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <alias alias="dc" type="POC.Service.DataAccess.DataController, POC.Service.DataAccess"/>
    <alias alias="sBP" type="POC.Service.Business.Process.StakeholderBP, POC.Service.Business.Process"/>
    <alias alias="pocs" type="POC.Service.Services.POCService, POC.Service.Services"/>
    <container>
      <register type="pocs">
        <property name="BP" dependencyType="sBP" />
      </register>
      <register type="sBP">
        <property name="DataController" dependencyType="dc" />
      </register>
    </container>
  </unity>
So just to clarify my understanding of things.  If I declare an object within code somewhere (like I was doing), Unity will not set the properties of that object (if defined in config), correct?