In the previous post, I used unity in order to implement interception in an application. The implementation was very easy but at the same time very limited. For example, there is no way to limit the methods or namespaces we want to intercept.
Policy Injection provides various ways of creating matching rules. My favorite rules are Namespace Matching Rule and Member Name Matching Rule. As the names imply, the former is defining the matching namespace(s) and latter is defining the name of the member(s).
Any combination of the policies can provide us with a powerful interception rule. Policies are defined in policies section of interception as below:
<interception>
<policies>
<add name="Policy">
<matchingRule name="MatchMethods" type="MemberNameMatchingRule">
<constructor>
<param name="namesToMatch">
<array type="string[]">
<value value="Calculate*" />
<!-- <value value="Other rules" /> -->
</array>
</param>
</constructor>
</matchingRule>
<matchingRule name="NameSpaceMatch" type="NamespaceMatchingRule">
<constructor>
<param name="namespaceName" value="AopTest" />
</constructor>
</matchingRule>
<callHandler name="Handler" type="Infrastructure.LoggingInterceptionHandler, Infrastructure">
<lifetime type="singleton"/>
</callHandler>
</add>
</policies>
</interception>
In the above rule, any method starts with "Calculate" and has the namespace of AopTest will be intercepted. We need to have a call handler that is very similar to our interception class in previous post. It implements ICallHandler and handles the interception:
namespace Infrastructure
{
public class LoggingInterceptionHandler : ICallHandler
{
private readonly ILogger logger;
public int Order { get; set; }
public LoggingInterceptionHandler(ILogger logger)
{
this.logger = logger;
}
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
var traceId = System.Guid.NewGuid().ToString();
this.logger.log(traceId, input);
var result = getNext()(input, getNext);
if (result.Exception != null)
{
this.logger.log(traceId, result.Exception, input);
}
else
{
this.logger.log(traceId, result.ReturnValue, input);
}
return result;
}
}
}
To start using the functionality, everything is almost the same as previous post. In this example we load configuration from "InterceptionPolicy" node:
var fileMap = new System.Configuration.ExeConfigurationFileMap { ExeConfigFilename = "unity.config" };
Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
var unitySection = (UnityConfigurationSection)configuration.GetSection("unity");
var uc = new UnityContainer().LoadConfiguration(unitySection, "InteceptionPolicy");
var calculator = uc.Resolve();
calculator.CalculateAdd(1, 2);
The complete config file should look something like below:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration, Version=2.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<section name="policyInjection" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.Configuration.PolicyInjectionSettings, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</configSections>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" />
<!-- Using Policy Injection -->
<container name="InteceptionPolicy">
<extension type="Interception" />
<register type="AopTest.ILogger, AopTest" mapTo="AopTest.Logger, AopTest" />
<register type="AopTest.ICalculator, AopTest" mapTo="AopTest.Calculator, AopTest">
<interceptor type="InterfaceInterceptor" />
<interceptionBehavior type="PolicyInjectionBehavior" />
</register>
<interception>
<policies>
<add name="Policy">
<matchingRule name="MatchMethods" type="MemberNameMatchingRule">
<constructor>
<param name="namesToMatch">
<array type="string[]">
<value value="Calculate*" />
<!-- <value value="Other rules" /> -->
</array>
</param>
</constructor>
</matchingRule>
<matchingRule name="NameSpaceMatch" type="NamespaceMatchingRule">
<constructor>
<param name="namespaceName" value="AopTest" />
</constructor>
</matchingRule>
<callHandler name="Handler" type="Infrastructure.LoggingInterceptionHandler, Infrastructure">
<lifetime type="singleton"/>
</callHandler>
</add>
</policies>
</interception>
</container>
</unity>
</configuration>