Dependency injection in WCF services
Background
In Dependency Injection class dependencies are injected at runtime. For achieving this code is done for abstractions and at the runtime concrete implementation are passed/injected either by constructor/property/method injection.
For example:
In typical 3 layered architecture Business access layer depends on Data access layer. So instead of referencing concrete Data access layer objects, BAL refers to the abstraction which is DAL contracts and inject dependency for contract at the runtime. This give way to loose coupling and DAL can be replaced at any time without rebuilding BAL as we only need to write new implementation of DAL contracts.
WCF
In WCF, we write services which are consumed by client. On the server side typically 3 layered architecture is followed where in Service refers to Business layer and business layer refers to Data access layer.
As discussed in previous post this sort of binding is hard binding and has potential drawbacks.
In WCF we need to have default constructor and cannot have parametrized constructor alone.
In order to inject dependency we can use a parametrized constructor and can implement our custom instance provider which will resolve to exact dependencies. Instance provider is responsible for creating WCF service instance, so we need to create custom behavior for instance provider and register the same with service.
Unity is used for dependency resolution in the same code.
Follow the following steps to implement same:
1. Implement custom Instance provider:
public class UnityInstanceProvider : IInstanceProvider
{
#region Properties
private readonly IUnityContainer container;
private readonly Type contractType;
#endregion
#region Constructors
public UnityInstanceProvider(IUnityContainer container, Type contractType)
{
this.container = container;
this.contractType = contractType;
}
#endregion
#region IInstanceProvider methods
public object GetInstance(System.ServiceModel.InstanceContext instanceContext, System.ServiceModel.Channels.Message message)
{
return GetInstance(instanceContext);
}
public object GetInstance(System.ServiceModel.InstanceContext instanceContext)
{
return container.Resolve(contractType);
}
public void ReleaseInstance(System.ServiceModel.InstanceContext instanceContext, object instance)
{
container.Teardown(instance);
}
#endregion
}
2. Implement custom service behavior for instance provider:
public class UnityServiceBehaviour : IServiceBehavior
{
#region Constants
private const string METADATA_EXCHANGE_CONTRACT = "IMetadataExchange";
private const string UNITY_CONTAINER_NAME = "unity";
#endregion
#region Static fields
private static IUnityContainer container;
#endregion
#region Constructors
static UnityServiceBehaviour()
{
container = new UnityContainer();
var section = (UnityConfigurationSection)ConfigurationManager.GetSection(UNITY_CONTAINER_NAME);
section.Containers.Default.Configure(container);
}
#endregion
#region IServiceBehavior methods
public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
Type serviceType = serviceDescription.ServiceType;
foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
{
if (endpointDispatcher.ContractName != METADATA_EXCHANGE_CONTRACT)
{
endpointDispatcher.DispatchRuntime.InstanceProvider = new UnityInstanceProvider(UnityServiceBehaviour.container, serviceType);
}
}
}
}
public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
}
#endregion
}
3. Implement custom behavior extension so that service behavior can be registered through behavior extensions in config.
public class UnityServiceBehaviorExtensionElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(UnityServiceBehaviour); }
}
protected override object CreateBehavior()
{
return new UnityServiceBehaviour();
}
}
4. Register behavior extension in config file:
<extensions>
<behaviorExtensions>
<add name="unityBehaviorExtension" type="WCFBehaviourExtensions.UnityServiceBehaviorExtensionElement, WCFBehaviourExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
5. Add this in service behavior.
6. Now you need to resolve constructor defined in the WCF service to parameterized with interfaces resolving to concrete classes in config file.
For unity the configuration will look like:
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<typeAliases>
<typeAlias alias="IDAL" type="<<Fully qualified name of DAL Contract>>, <<Name of dll>>" />
<typeAlias alias="DAL" type="<<Fully qualified name of DAL concrete class>>, << Name of dll>>" />
<typeAlias alias="Controller" type="<<Fully qualified name of Controller concrete class>>, <<Name of dll>>" />
<typeAlias alias="IController" type="<<Fully qualified name of Controller contract>>, <<Name of dll>>" />
<typeAlias alias="ServiceClass" type="<<Fully qualified name of Service Class>>, <<Name of dll>>" />
</typeAliases>
<containers>
<container>
<register type="IController" mapTo="Controller" >
<constructor>
<param name="<<ConstructorParameterName>>" >
<dependency/>
</param>
</constructor>
</register>
<register type="IDAL" mapTo="DAL" >
</register>
<register type="ServiceClass" mapTo="ServiceClass" >
<constructor>
<param name="<<ConstructorParameterName>>">
<dependency/>
</param>
</constructor>
</register>
</container>
</containers>
</unity>
Now your service has all dependencies injected.
Happy coding!!!!
Comments
Post a Comment