Matching rules and interceptors : cannot make it work

Jan 16, 2012 at 2:41 AM
Edited Jan 16, 2012 at 2:41 AM

Dear all,

I try to apply an interceptor to a list of objets managed by unity.

Here is what I did :

1. Add nuget reference to Unity interceptions extensions (in addition to Unity itself).

2. Create a simple implementation of ICallHandler

3. Add the matchingRule (AssemblyMatchingRule or NamespaceMatchingRule) and the handler in the unity xml config. The unity xml config is ok (sectionExtension and <extension type="Interception"/> directive) because local interceptors (defined within the registration of a type with the <register/> tag) work.

With handler and matching rule : nothing  happens, the constructor of my handler is never invoked.

The handler itself (with or without [ConfigurationElementType(typeof(CustomCallHandlerData))], it does not make any difference) :

 

    public class TraceCallHander : ICallHandler
    {
        public TraceCallHander()
        {
            Console.WriteLine("inside traceCall handler");
        }
        IMethodReturn ICallHandler.Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            Console.WriteLine("about to invoke " + input.MethodBase.Name + " with args " + input.Arguments );
            IMethodReturn result = getNext()(input, getNext);
            Console.WriteLine("after invoke");
            return result;
        }

        int ICallHandler.Order { get; set; }
    }

 

The configuration block :

    <container>
      
      <extension type="Interception"/>
      
      <interception>
        <policy name="TracePolicy">
          <matchingRule name="Assembly Matching Rule" type="AssemblyMatchingRule">
            <constructor>
              <param name="assemblyName" value="MyAssembly"/>
            </constructor>
          </matchingRule>
          <callHandler  type="MyAssembly.MyNamespace.TraceHandler, MyAssembly" name="tracer"/>
        </policy>
      </interception>

      <register type="MyAssembly.MyNamespace.IFoo, MyAssembly" mapTo="MyAssembly.MyNamespace.FooImpl, MyAssembly" name="target">
      </register>
    </container>

 

I tried to add

<interceptors>
  <interceptor type="VirtualMethodInterceptor"/>
</interceptors>

inside the <container> element but it does not help. I tried to add "<interceptor type="VirtualMethodInterceptor"/> under the register element but it does not help. I tried other type/instance interceptor (since FooImpl has a virtual method and implements an interface, both should work).

Of course I try with IFoo foo = container.Resolve<IFoo>("target");

Then I call a method named 'Bar' which just write "inside bar".

The handler is never instantiate; the method Bar is never intercepted, and even if I use a custom rule with a 'Console.WriteLine("inside rule constructor") I never see this message.

If I add  the <policyInjection/> inside the <register/> element in addition to <interceptor type="..."/> element it crashs.

The stack trace :

 

Resolution of the dependency failed, type = "MyAssembly.MyNamespace.IFoo", name = "target".
Exception occurred while: while resolving.
Exception is: ResolutionFailedException - Resolution of the dependency failed, type = "Microsoft.Practices.Unity.InterceptionExtension.InjectionPolicy", name = "Microsoft.Practices.Unity.InterceptionExtension.AttributeDrivenPolicy, Microsoft.Practices.Unity.Interception, Version=2.1.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The type InjectionPolicy cannot be constructed. You must configure the container to supply this value.
-----------------------------------------------
At the time of the exception, the container was:

  Resolving Microsoft.Practices.Unity.InterceptionExtension.InjectionPolicy,Microsoft.Practices.Unity.InterceptionExtension.AttributeDrivenPolicy, Microsoft.Practices.Unity.Interception, Version=2.1.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35

-----------------------------------------------
At the time of the exception, the container was:

  Resolving MyAssembly.MyNamespace.Foo,target (mapped from MyAssembly.MyNamespace.IFoo, target)
    Resolving Microsoft.Practices.Unity.InterceptionExtension.PolicyInjectionBehavior,(none)
    Resolving parameter "policies" of constructor Microsoft.Practices.Unity.InterceptionExtension.PolicyInjectionBehavior(Microsoft.Practices.Unity.InterceptionExtension.CurrentInterceptionRequest interceptionRequest, Microsoft.Practices.Unity.InterceptionExtension.InjectionPolicy[] policies, Microsoft.Practices.Unity.IUnityContainer container)
      Resolving Microsoft.Practices.Unity.InterceptionExtension.InjectionPolicy[],(none)

 

Any idea ? Thanks a lot,

Jan 16, 2012 at 7:34 AM
Edited Jan 16, 2012 at 7:36 AM

Why do you use the VirtualMethodInterceptor? That would only work if the methods you try to intercept are declared virtual. If you want Unity to intercept all calls that go through an interface (like your IFoo) I would try the InterfaceInterceptor.

Jan 16, 2012 at 2:22 PM

Hi, it does not make any difference because FooImpl implements IFoo and its method (Bar) is virtual so - but maybe I'm wrong - I can use instance interception or type interception.

I don't know which configuration is mandatory among the followings :

  • [ConfigurationElementType(typeof(CustomCallHandlerData))] on the ICallHander implementation ?
  • <policyInjection/> in the register element ?
  • <interceptors><interceptor type="..."/></interceptors> in the container element ?
  • <interceptor type=".."/> in the register element ?
Jan 16, 2012 at 2:38 PM

You need to register the interceptor as well as policyInjection:

<?xml version="1.0" encoding="utf-8" ?>
<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">
    <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" />
    <container>
      <extension type="Interception"/>
      <interception>
        <policy name="TracePolicy">
          <matchingRule name="Assembly Matching Rule" type="AssemblyMatchingRule">
            <constructor>
              <param name="assemblyName" value="MyAssembly"/>
            </constructor>
          </matchingRule>
          <callHandler name="TraceCallHander" type="MyAssembly.MyNamespace.TraceCallHander, MyAssembly" />
        </policy>
        </interception>

      <register type="MyAssembly.MyNamespace.IFoo, MyAssembly" mapTo="MyAssembly.MyNamespace.FooImpl, MyAssembly" name="target">
        <interceptor type="InterfaceInterceptor"/>
        <policyInjection/>
      </register>
    </container>
  </unity>
</configuration>

If you have a virtual method you can use InterfaceInterceptor or VirtualMethodInterceptor: both should work.

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

Jan 16, 2012 at 9:56 PM
Edited Jan 16, 2012 at 10:02 PM

Everything was ok.... except the way I was calling resolve :

        private static IUnityContainer container;
        private static IFoo foo;

static void Main(string[] args) { using (IUnityContainer unity = new UnityContainer()) { container = unity.LoadConfiguration(); } foo = container.Resolve("target"); foo.Bar(); Console.ReadKey(); }

Actually it works, even with interception configured on individual types. But when you add <policyInjection/> to enable matching rules based interceptions : it crashs.

Put foo = container.Resolve<IFoo>("target") inside the using block and it works.

But it would be great to make optional the

        <interceptor type="InterfaceInterceptor"/>
        <policyInjection/>

on each type. The idea behind AOP is that the matching rule is enough to weave the relations between advices (CallHander) and targets, here it seems that we have to activate interception on each type, one by one (but maybe I missed something). How about replacing this 'opt-in' approach by an 'opt-out' where :

  • every type would be (by default) candidate for interception and a tag <disable-interception/> could be used inside the <type/> declaration.
  • a global interceptor strategy could apply (for example InterfaceInterceptor when possible, VirtualMethodInterceptor as a fallback), with possible override in individual <type/>

Just a kind of 'convention over configuration' principle.

Jan 16, 2012 at 10:12 PM

I understand what you are saying.  

You can create a Container Extension to register interceptors automatically.  See this answer for an example.

 

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