Registering Policy Injection Components

This topic explains how to register the various elements, including interceptors, behaviors, policies, call handlers, and matching rules that Unity uses to configure a container for interception and for a policy injection behavior.

When you configure policy injection you must specify which objects will be intercepted with the policy injection behavior and which policies in the container are to be used. Then when building the object, the policy injection behavior is set up using the policies already defined in the container.

This topic contains the following sections:

  • Policy Injection Run-Time Configuration
  • Defining Policies by Using the API

Policy Injection Run-Time Configuration

There are two steps to configuring a type for policy injection. First, you must register the type in the container. In that registration, you must configure an interceptor and enable the PolicyInjectionBehavior. Second, you must configure the policy injection policies that determine which call handlers execute on which methods.

int intercepted = 0;
var container = new UnityContainer();
container
    .AddNewExtension<Interception>()
    .RegisterType<ActionCallHandler>()
    // Register the type to be intercepted
    .RegisterType<InterceptedType>(
            new Interceptor<TransparentProxyInterceptor>(),
            new InterceptionBehavior<PolicyInjectionBehavior>())
    // Configure policies
    .Configure<Interception>()
        .AddPolicy("policy")
            .AddCallHandler(new ActionCallHandler(() => intercepted++))
            .AddMatchingRule(new MemberNameMatchingRule("MethodX"));
'Usage
Dim intercepted As Integer = 0
Dim container = New UnityContainer()
container _
    .AddNewExtension(Of Interception)() _
    .RegisterType(Of ActionCallHandler)() _
    ' Register the type to be intercepted
    .RegisterType(Of InterceptedType)( _
            New Interceptor(Of TransparentProxyInterceptor)(), _
            New InterceptionBehavior(Of PolicyInjectionBehavior)()) _
    ' Configure policies
    .Configure(Of Interception)() _
        .AddPolicy("policy") _
            .AddCallHandler (New ActionCallHandler(Function() _
             System.Math.Max(System.Threading.Interlocked.Increment _
                 (intercepted),intercepted - 1)))

Defining Policies by Using the API

The streamlined InterceptionExtension.PolicyDefinition APIs provide a simplified way to wire up RuleDriven policies and their IMatchingRules and ICallHandlers. The general-purpose APIs require repeated calls to the RegisterType method. The streamlined extension APIs reduce the overhead required to manage the various strings and cross links, thus making the process more obvious and convenient.

Everything you can do with the InterceptionExtension.PolicyDefinition API can be done with the general-purpose APIs.

For more information about interception and selecting the objects and their members to add a handler pipeline, see Using Interception and Policy Injection.

The following are streamlined configuration InterceptionExtension.PolicyDefinition methods:

  • AddPolicy. These methods are a set of methods on the interception type.
  • AddMatchingRule. These methods are on the PolicyDefinition class you get when you call AddPolicy.
  • AddCallHandler. These methods are on the PolicyDefinition class you get when you call AddPolicy.

These methods are only used for configuring rule-driven policies, which are also the only policies configurable with the standard installation and setup. For user-defined policies, you must use the general-purpose APIs.

The streamlined InterceptionExtension.PolicyDefinition API is similar to the expanded RegisterType API in that you can provide a lifetime manager (just like RegisterType), the mapping, and the injection configuration. It also differs in the following ways:

  • The entry point for the API is the AddPolicy method in the interception extension.

  • The result of this method is a transient PolicyDefinition object, which can be used to add matching rules and add call handlers. These methods add rules and handlers to the policy, but they also configure the container as necessary.

  • The signatures for these methods are similar to those of RegisterType, but the names imply the interface being registered instead of relying on generic type parameters, as with the general-purpose RegisterTypeMethods. The following is an example:

    // Instead of:
    RegisterType<ICallHandler, MyCallHandler>(...) 
    // you use:
    AddCallHandler<MyCallHandler>(...).
    
    'Usage
    ' Instead of:
    RegisterType(Of ICallHandler, MyCallHandler)(...) 
    ' you use:
    AddCallHandler(Of MyCallHandler)(...).
    

There are three approaches for using the streamlined interception methods:

  • Supply a string parameter. Use a string parameter to indicate that you want to use an object that was configured elsewhere. If you used the general-purpose API to configure a handler in the container, you would just link to it with this approach.

    Note

    You can supply a string and configure the corresponding rule or handler at a later time.

    The following is an example of a policy with externally configured rules and handlers.

    public void PolicyUseExample()
    {
        IUnityContainer container = new UnityContainer();
        container.AddNewExtension<Interception>();
        container
            .Configure<Interception>()
                .AddPolicy("MyPolicy")
                .AddMatchingRule("rule1")
                .AddCallHandler("handler1")
                .AddCallHandler("handler2")
        .Interception.Container
                .RegisterType<IMatchingRule, AlwaysMatchingRule>("rule1")
            .RegisterType<ICallHandler,LogCallHandler>(
                "handler1",
                new InjectionConstructor("handler1"))
            .RegisterType<ICallHandler,LogCallHandler>(
                "handler2",
                new InjectionConstructor("handler2"),
                new InjectionProperty("Order", 10));
            .RegisterType<TypeToIntercept>("wrappable",
                new Interceptor<TransparentProxyInterceptor>(),
                new InterceptionBehavior<PolicyInjectionBehavior>());
    
        LogCallHandler.Calls.Clear();
        var wrappable1 = container.Resolve<TypeToIntercept>("wrappable");
        wrappable1.Method2();
    }
    
    'Usage
    Public Sub PolicyUseExample()
        Dim container As IUnityContainer = New UnityContainer()
        container.AddNewExtension(Of Interception)()
        container _
            .Configure(Of Interception)() _
                .AddPolicy("MyPolicy") _
                .AddMatchingRule("rule1") _
                .AddCallHandler("handler1") _
                .AddCallHandler("handler2") _
        .Interception.Container _
                    .RegisterType(Of IMatchingRule, AlwaysMatchingRule) _
                              ("rule1") _
                .RegisterType(Of ICallHandler, LogCallHandler) _
                         ("handler1", New InjectionConstructor("handler1")) _
                .RegisterType(Of ICallHandler, LogCallHandler) _
                     ("handler2",  _
                     New InjectionConstructor("handler2"),  _
                     New InjectionProperty("Order", 10)) _
            .RegisterType(Of TypeToIntercept)("wrappable", _
                new Interceptor(Of TransparentProxyInterceptor)(), _
                new InterceptionBehavior(Of PolicyInjectionBehavior)())
    
        LogCallHandler.Calls.Clear()
        container _
               .Configure(Of Interception)() _
                 .SetInterceptorFor(Of TypeToIntercept) _
                   ("wrappable", New TransparentProxyInterceptor())
        Dim wrappable1 As TypeToIntercept = container.Resolve(Of TypeToIntercept)("wrappable")
        wrappable1.Method2()
    End Sub
    
  • Supply an instance. Use this case when you already have the object and want only the new policy to use it.

    public void APolicyGivenRulesAndHandlers()
    {
        IUnityContainer container = new UnityContainer();
        container.AddNewExtension<Interception>();
        IMatchingRule rule1 = new AlwaysMatchingRule();
        ICallHandler handler1 = new CallCountHandler();
        container
            .Configure<Interception>()
                .AddPolicy("MyPolicy")
                .AddMatchingRule(rule1)
                .AddCallHandler(handler1);
            .RegisterType<TypeToIntercept>("wrappable",
                new Interceptor<TransparentProxyInterceptor>(),
                new InterceptionBehavior<PolicyInjectionBehavior>());
        var wrappable1 = container.Resolve<TypeToIntercept>("wrappable");
        wrappable1.Method2();
    }
    
    'Usage
    Public Sub APolicyGivenRulesAndHandlers()
        Dim container As IUnityContainer = New UnityContainer()
        container.AddNewExtension(Of Interception)()
        Dim rule1 As IMatchingRule = New AlwaysMatchingRule()
        Dim handler1 As ICallHandler = New CallCountHandler()
        container _
            .Configure(Of Interception)() _
                .AddPolicy("MyPolicy") _
                .AddMatchingRule(rule1) _
                .AddCallHandler(handler1)
        container.RegisterType(Of TypeToIntercept)("wrappable",
            new Interceptor(Of TransparentProxyInterceptor)(), _
            new InterceptionBehavior(Of PolicyInjectionBehavior)())
        Dim wrappable1 = container.Resolve(Of TypeToIntercept)("wrappable") 
        wrappable1.Method2()
    
    End Sub
    
  • Supply a type. Supply a type either as a generic type parameter or a normal parameter, and, optionally, a name, a lifetime container, and injection configuration in any combination. Use this if the matching rule or call handler object is unique to the policy it is defined in, as it allows you to centralize the configuration of the policy in one spot. Normally, you do not need to specify a name for the matching rules or call handlers. If you do, that name can be used in other policies to reuse the named matching rule or call handler using the configuration approach, as shown in supply a string parameter. In this approach, you describe how to resolve for injection.

    public void APolicyGivenRulesAndHandlersTypes()
    {
        IUnityContainer container = new UnityContainer();
        container.AddNewExtension<Interception>();
        container
           .Configure<Interception>()
                .AddPolicy("MyPolicy")
                    .AddMatchingRule(typeof(AlwaysMatchingRule)),
                        new InjectionConstructor("rule1"))
                    .AddCallHandler(typeof(LogCallHandler)),
                        new InjectionConstructor("handler1"),
                        new InjectionProperty("Order", 10))
        container.RegisterType<TypeToIntercept>("wrappable",
            new Interceptor<TransparentProxyInterceptor>(),
            new InterceptionBehavior<PolicyInjectionBehavior>());
        LogCallHandler.Calls.Clear();
        TypeToIntercept wrappable1 = container.Resolve<TypeToIntercept>("wrappable");
        wrappable1.Method2();
    }
    
    'Usage
    Public Sub APolicyGivenRulesAndHandlersTypes()
        Dim container As IUnityContainer = New UnityContainer()
        container.AddNewExtension(Of Interception)()
        container.Configure(Of Interception)() _
            .AddPolicy("MyPolicy") _
            .AddMatchingRule(GetType(AlwaysMatchingRule)),
                new InjectionConstructor("rule1")) _
            .AddCallHandler(GetType(LogCallHandler)),
                new InjectionConstructor("handler1"), _
                new InjectionProperty("Order", 10)) 
        container.RegisterType(Of TypeToIntercept)("wrappable", _
            new Interceptor(Of TransparentProxyInterceptor)(), _
            new InterceptionBehavior(Of PolicyInjectionBehavior)()) 
        LogCallHandler.Calls.Clear()
        Dim wrappable1 As TypeToIntercept = _
            container.Resolve(Of TypeToIntercept)("wrappable") 
        wrappable1.Method2()
    End Sub
    

One significant difference between the RegisterType methods and this streamlined API is that when you do not specify a name, a name is generated for you, so all anonymous definitions without names will not be overwritten by other definitions without names. If you do specify a name, it is used and may override previous existing policies with the same name.

You can use generic parameter support when you configure for injection, just like you do with RegisterType.

The generic versions of the AddMatchingRule and AddCallHandler methods contain the types you can supply, unlike RegisterType. RegisterType is a general-purpose method and places no constraints on the types you can supply. For AddCallHandler<TCallHandler>(), you can only provide a type value for TCallHandler that implements ICallHandler. The non-generic version of the method is limited to performing run-time checks. Similarly, AddMatchingRule<TRule>() is constrained to types implementing IMatchingRule.

Once you have defined a policy, you must also set up an interceptor and turn on policy injection for each type you want intercepted. Setting the interceptor just says how to do interception on a type if it has members that match any rules. Without the policy injection behavior, rules will not be checked. And you must have at least one matching rule defined or nothing will happen.

In general, you do not want to use AlwaysMatchingRule, since that matches absolutely everything, which is rather indiscriminate (and is only part of the test suite, not the main .dll file). It is useful as a shortcut for testing purposes; but, using the Type or Namespace matching rules is better for more generalized matches.