Interception to methods of different classes in an application

Jul 14, 2012 at 7:12 PM
Edited Jul 14, 2012 at 7:12 PM

Hi,

I am quite new to Unity. As I figured out it is possible to do interception to objects of classes which are registered to UnityContainer in a manner as   

  IUnityContainer container = new UnityContainer();
          container.AddNewExtension<Interception>();
          container.RegisterType<ISubscriberRepository, SubscriberRepository>();
          container.Configure<Interception>().SetInterceptorFor<ISubscriberRepository>(
               new InterfaceInterceptor()
               );
          ISubscriberRepository repository = container.Resolve<ISubscriberRepository>();

 

  My requirement is to be able to mark several methods in my application with my custom attibutes which I derive from InterceptionExtention.HandlerAttribute.
After that it would be great if I would not need to register each and every single type as e.g.


   container.RegisterType<ISubscriberRepository, SubscriberRepository>();
          container.Configure<Interception>().SetInterceptorFor<ISubscriberRepository>(
               new InterfaceInterceptor()
               );


as I need to be very flexible in my application. Imagine I have a NullHandlerAttribute which I need to place on specific methods during ongoing development phase.
Requiering to always enhance the setup doing container.RegisterType would be quite tedious.
Is there a way to avoid that?

Another question: If my application itself must not know about Unity. Is there a way to instantiate the objects without using container.Reslove<..>?

Regards Peter

 


Jul 15, 2012 at 7:25 AM

You could create a UnityContainerExtention to automatically setup the interceptions you want.  See this post.  In your case the extension would look something like this:

    public class UnityInterfaceInterceptionRegisterer : UnityContainerExtension
    {
        protected override void Initialize()
        {
            base.Container.AddNewExtension<Interception>();
            base.Context.Registering += new EventHandler<RegisterEventArgs>(this.OnRegister);
        }

        private void OnRegister(object sender, RegisterEventArgs e)
        {
            IUnityContainer container = sender as IUnityContainer;

            if (e != null && e.TypeFrom != null && e.TypeFrom.IsInterface)
            {
                container.Configure<Interception>()
                    .SetInterceptorFor(e.TypeFrom, e.Name, new InterfaceInterceptor());
            }
        }
    }

Then the main code would be:

    IUnityContainer container = new UnityContainer()
        .AddNewExtension<UnityInterfaceInterceptionRegisterer>();

    container.RegisterType<ISubscriberRepository, SubscriberRepository>();

    ISubscriberRepository repository = container.Resolve<ISubscriberRepository>();

If you don't want to add interception to all types that are registered as interfaces then you could add a custom attribute to all classes that you wish to opt-in for interface interception and then query that information from the TypeTo. 

For question 2: when you use container.Resolve<> to retrieve object instances this is called using the service locator pattern.  Some people consider this an anti-pattern.  So the way to avoid the use of container.Resolve and coupling your code to the Unity container is to use the composition root pattern.  The idea is to compose the entire object graph as close to the application entry point as possible.  

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

Jul 15, 2012 at 11:17 AM

Hi Randy,

thanks for that hint, especially the sample fitting first part of my question helps me a lot.
So I do not need to call container.Configure<Interception>().SetInterceptorFor<IMyType>(... explicitely any more.
Registering all the types before still is necessary but I hope to find a suitable solution for that as well.
Regards to the second part of my question, the container.Resolve<..> issue, I am searching around the web a while in order to gain some knowledge
about composition root pattern you mention.

This might help me to register all the types to the container very early if not initially.
Nevertheless I wonder whether there somehow is a possibility to
1. new up our objects as we do until now in our app
2. making mentioned HandlerAttributes combined with the related ICallHandler implementors intercepting successfully.

It is simply not possible to change the whole application to use container.Reslove<..> where we now have the direct CTor call all over.
That is a big issue for me and it would be great if there would be a solution for that.

Regards Peter

Jul 18, 2012 at 5:25 AM

Newing up objects goes against the grain of dependency injection; the idea is to register all dependencies with the container and when your object is resolved all concrete dependencies are injected, creating the object graph.

For example, let's say you have a top level service such as OrderService.  Let's say it is dependent on a logger (ILogger), some way to send notifications (INotifier), and an Inventory Service (IInventoryService).  When the OrderService is needed it would be resolved from the container with the ILogger, INotifier, and IInventoryService concrete instances injected into the OrderService.  If you already have an OrderService that is newing up a Logger, Notifier and InventoryService then you would have to change the code to accept those instances (contructor/method/property injection) instead of directly instantiating them.

Depending on the existing design it might be easy to just add a new constructor that accepts the dependencies, and tell Unity to use that constructor.

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

 

Jul 19, 2012 at 5:53 AM

Hi Randy,

thanks for your answer. I guess the principles and advantages of doing so I understood already. My issue is that we deal with a let's say "naturally grown", quite big application, which already is shipped since more than two years. Changing the coding in order to base on this container principle all over is not feasible at all, although this would provide us with some advantages as e.g. loose coupling.

My concrete requirement is to be able to intercept some methods which I would like to makre by an attribute in different classes of the application and I was wondering whether there is a way to achieve that even without requiring to initialize relevant classes using the container. But If I get you right, there is no way at all to achieve this!?

Regards and thanks
Peter

Jul 19, 2012 at 3:11 PM

The effort required to retrofit "pure" dependency injection/inversion of control into your application would depend on the specific design of the application.  At some point the container needs to "get in the way" to create and compose the objects.  I realize that this could be a large effort so you could do a cost/benefit analysis to see if the potential gains outweigh the effort.

For Unity/Policy Injection to setup interception then the container needs to create the object.  

One alternative to using Unity for interception is to inherit from MarshalByRefObject or ContextBoundObject. My impression is that this approach was more popular in the earlier days of .NET.  See http://www.developerfusion.com/article/5307/aspect-oriented-programming-using-net/3/ .

Another alternative might be to use a weaving approach (instead of the proxy approach of the container) that inserts the desired functionality.  PostSharp seems to be very popular in this space but you could also look at some free options such as LOOM.NET (doesn't seem very popular but has active development).

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

Jul 19, 2012 at 3:41 PM
Edited Jul 20, 2012 at 7:23 AM

Hi Randy,

thanks a lot for your help. Probably I will investigate on effort of changing the applications design - which will be much. In doubt I will see wehter there are other approaches which perhaps are not as flexible and elegant as using unity but in the given case less time consuming.

Thanks a lot for your answers, I really appreciate that!

Regards
Peter