Thursday, July 15, 2010

Policy Injection: WCF Instance Provider

Policy Injection is one of Enterprise Application Blocks, which can be used to perform AOP(Aspect Oriented Programming). In this blog I will discuss about using Policy Injection for WCF services. For this to happen your WCF service objects should be created using Policy Injection. WCF provides behavior extension points to customize various aspects. We will see how to provide custom instance provider which will be used by WCF to create WCF objects.

NOTE: In order for a class to be instantiable through Policy Injection it has to either implement a interface or it should be derived from MarshalByRef class

In scenarios where you want to intercept WCF calls and apply policies defined in Policy Injection, you need to provide your custom WCF instance provider which will use Policy Injection application block to create instances of your WCF services.

You need to devise custom instance provider and implement custom behavior to use this custom instance provider. Once you define custom behavior you can apply this behavior to your WCF service end points.

Custom WCF instance provider must implement IInstanceProvider.

WCF Instance Provider:

public class PolicyInjectionInstanceProvider:IInstanceProvider
{
private Type serviceContractType { get; set; }
private static readonly IUnityContainer container;
private readonly object _sync=new object();
private static readonly TransparentProxyInterceptor injector = new TransparentProxyInterceptor();

static PolicyInjectionInstanceProvider()
{
container = new UnityContainer()
.AddNewExtension();

IConfigurationSource configSource = ConfigurationSourceFactory.Create();
PolicyInjectionSettings settings = (PolicyInjectionSettings)configSource.GetSection(PolicyInjectionSettings.SectionName);
if (settings != null)
{
settings.ConfigureContainer(container, configSource);
}
}

public PolicyInjectionInstanceProvider(Type t)
{
if (t != null && !t.IsInterface)
{
throw new ArgumentException("Specified type must be an interface.");
}
this.serviceContractType = t;
}

#region IInstanceProvider Members

public object GetInstance(System.ServiceModel.InstanceContext instanceContext, System.ServiceModel.Channels.Message message)
{
Type type = instanceContext.Host.Description.ServiceType;

if (serviceContractType != null)
{
lock (_sync)
{
container.Configure().SetDefaultInterceptorFor(serviceContractType, injector);
container.RegisterType(serviceContractType, type);
return container.Resolve(serviceContractType);
}
}
else
{
if (!type.IsMarshalByRef)
{
throw new ArgumentException("Type must inherit from MarhsalByRefObject if no ServiceInterface is Specified.");
}
lock (_sync)
{
container.Configure().SetDefaultInterceptorFor(type, injector);
return container.Resolve(type);
}
}
}

public object GetInstance(System.ServiceModel.InstanceContext instanceContext)
{
return GetInstance(instanceContext, null);
}

public void ReleaseInstance(System.ServiceModel.InstanceContext instanceContext, object instance)
{
IDisposable disposable = instance as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}

#endregion
}

Custom Behavior Extension Element:

public class PolicyInjectionBehavior : BehaviorExtensionElement, IEndpointBehavior
{
public override Type BehaviorType
{
get { return typeof(PolicyInjectionBehavior ); }
}

protected override object CreateBehavior()
{
return new PolicyInjectionBehavior ();
}
#region IEndpointBehavior Members

public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{

}

public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{

}

public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
Type contractType = endpoint.Contract.ContractType;
endpointDispatcher.DispatchRuntime.InstanceProvider = new PolicyInjectionInstanceProvider(contractType);

}

public void Validate(ServiceEndpoint endpoint)
{

}

#endregion
}

Import above defined behavior in web.config using behavior extensions provided by WCF. Note that type attribute should provide fully qualify name (and be careful with spaces), along with assembly and version number.

<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="policyInjectionInstanceProvider" type="policyInjectionBehavior, assembly_name, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior name="PolicyInjectionProviderBehavior">
</behavior>
</endpointBehaviors>
</behaviors>
</System.serviceModel>

Once you import this custom behavior extension, you can use this as behavior in your service model and specify this behavior in service end points.

Whenever client tries to access your WCF service, service object will be created using PolicyInjectionInstanceProvider class.

No comments: