3

In this scenario I my application is handed an already initialized UnityContainer on which has been registered a type which boils down to this:

container.RegisterType<IService>(new InjectionFactory(c => new Service()));

What I need to achieve is adding an interceptor ServiceInterceptor to the IService registration. I suppose the obvious answer is: Do this by running a second RegisterType<IService> and applying the interceptor as injection members. However, re-creating the provided injection factory and delegate as described below is unfortunately not feasible. The new Service() statement isn't available to me at this point.

container.RegisterType<IService>(
    new InjectionFactory(c => new Service()),
    new Interceptor<InterfaceInterceptor>(),
    new InterceptionBehavior<ServiceInterceptor>());

So: I am looking for a way to add further injection members to an existing ContainerRegistration.

// 1. Get the current container registration
var containerRegistration = container.Registrations
    .First(cr => cr.RegisteredType == typeof(IService));

// 2. Is this even possible?
ApplyInterception(
    containerRegistration,
    new Interceptor<InterfaceInterceptor>(),
    new InterceptionBehavior<ServiceInterceptor>());

// 3. Profit!
BartoszKP
  • 34,786
  • 15
  • 102
  • 130
Sigurd Garshol
  • 1,376
  • 3
  • 15
  • 36

1 Answers1

4

You could initially register the type as a named registration (using the InjectionFactory) while also providing a default registration (with no name) that just resolves the named registration:

container.RegisterType<IService>("original",
                      new InjectionFactory(c => new Service()));
container.RegisterType<IService>(
                      new InjectionFactory(c => c.Resolve<IService>("original")));

So you can resolve IService as you would normally do. However you will now be able to replace the default registration while keeping the original named registration. This way you can work around your issue, where you couldn't re-register IService due to the factory statement not being available at that point.

With this approach in place, at a later point you can override the default IService registration with one where interception is registered and still uses the original named registration for resolving the instance:

container.RegisterType<IService>(
    new InjectionFactory(c => c.Resolve<IService>("original")),
    new Interceptor<InterfaceInterceptor>(),
    new InterceptionBehavior<ServiceInterceptor>());

If you now resolve IService, you will still use the original factory method c => new Service() as it is resolving the "original" named registration, but this time your ServiceInterceptor is also applied.

I have created this fiddle so you can check a full working example.


There is a second approach using Policy Injection. (See Policy Injection section in the msdn).

First configure your type as usual, but leave the door opened for using Policy Injection:

container.RegisterType<IService>(
    new InjectionFactory(c => new Service()),       
    new InterceptionBehavior<PolicyInjectionBehavior>(),
    new Interceptor<InterfaceInterceptor>());

At this point your service is registered without any interception being applied. However at a later point you can add a policy injection rule, for example matching your service type name, that adds the interception:

container.Configure<Interception>()
    .AddPolicy("yourInterceptor")
    .AddMatchingRule<TypeMatchingRule>
        (new InjectionConstructor("MyNamespace.Service", true))
    .AddCallHandler<ServiceInterceptorHandler>(
        new ContainerControlledLifetimeManager(),
        new InjectionConstructor(),
        new InjectionProperty("Order", 1));

Now if you resolve IService, the interception logic in ServiceInterceptorHandler will be applied (This class is basically the same as ServiceInterceptor in the first approach, but implementing ICallHandler instead of IInterceptionBehavior)

Again, check the example in this fiddle


Having a look at both options, I personally feel more comfortable with the first approach, avoiding the overhead of the matching rules.

The first approach would also allow you to easily completely turn off interception by overriding again the IService registration, saving you from the interceptors overhead if you want it completely off. (Both approaches allow you to implement the WillExecute property of the interceptor/handler classes, but you still have the overhead of the interceptors). You can do this using policy injection, but you need another intermediate call handler, see this post

However with the second approach, you could apply this solution to multiple classes using the matching rules (For example all classes in a namespace, or all classes whose name follows a specific pattern, etc. You can take a look at the matching rules here)

In the end you will need to decide which one fits you best. (and there might be other approaches, would love to see them posted!)

Daniel J.G.
  • 34,266
  • 9
  • 112
  • 112
  • Thanks for the tip - naming the type registration seems so obvious now :) I added a named type registration that included an injection factory resolving the original unnamed registration (i.e. the oposite of your suggestion), and added the interceptors to the new named registration. This seems to work nicely. But is there truly no way to access and modify current type registration entries? -S – Sigurd Garshol Oct 31 '14 at 12:26
  • I don't know of any way of modifying the current registration other than overriding it. Certainly not through the `ContainerRegistration` class whose members are read only. – Daniel J.G. Oct 31 '14 at 12:41
  • There is also the option of using [child containers](http://msdn.microsoft.com/en-us/library/dn507462(v=pandp.30).aspx) but that is essentially another way of overriding the initial configuration – Daniel J.G. Oct 31 '14 at 12:48
  • 1
    Yeah. The child container approach led me down a path that ended up in Nowheresville :) – Sigurd Garshol Oct 31 '14 at 13:05