The following article is part of the chapter 5 of my book, Mastering Ninject for Dependency Injection, [PACKT] publishing, 2013.
What is Interception?
There are cases where we need to do some operations before or after calling a single method or a number of methods. For example, we may need to log something before and after invoking all the methods in a component. Interception allows us to wrap the injecting dependency in a proxy object which can perform such operations before, after, around or instead of each method invocation. This proxy object can then be injected instead of the wrapped service. Ninject Interception extension creates such proxy wrappers on the fly and allows us to intercept invocations of the wrapped service members. The following diagram shows how a service will be replaced with an intercepted one during the interception process.
Interception is one of the best practices for implementing Cross Cutting Concerns such as logging, caching, exception handling, or transaction processing.
Setup Interception
Ninject Interception extension generates proxy instances based on the DynamicProxy implemented by LinFu or Castle. We can choose which implementation to use when referencing interception extension. Using NuGet either install Ninject.Extensions.Interception.DynamicProxy or Ninject.Extensions.Interception.Linfu. NuGet will also automatically install Ninject, Ninject.Extensions.Interception and Castle.Core or LinFu.DynamicProxy depending on the selected DynamicProxy implementation. In this section, we will use Castle DynamicProxy. We can also download and reference the binary files of these libraries manually. Finally we need to add the following using statements to the code:
using Ninject.Extensions.Interception; using Ninject.Extensions.Interception.Infrastructure.Language;
Member Interception
Once we have setup our project for Interception extension, some extension methods will be available via the kernel which can be used for interception. We can use these methods to intercept a method or property. Here are a few of them:
InterceptReplace<T> (Expression<Action<T>>, Action<IInvocation>) InterceptAround<T> (Expression<Action<T>>, Action<IInvocation>, Action<IInvocation>) InterceptBefore<T> (Expression<Action<T>>, Action<IInvocation>) InterceptAfter<T> (Expression<Action<T>>, Action<IInvocation>)
The following example shows us how to use a method interception to log around the GetAllCustomers() method of the CustomerService class:
Kernel.InterceptAround<CustomerService>( s=>s.GetAllCustomers(), invocation =>logger.Info("Retrieving all customers..."), invocation =>logger.Debug("Customers retrieved"));
In the preceding example, the type parameter indicates the service we are going to intercept (that is, CustomerService). The first parameter is a delegate which indicates the method to intercept (for example, GetAllCustomers). For the InterceptAround method, the second and third parameters are two delegates which will be executed before and after invoking the intercepted method respectively.
The invocation parameter whose type is IInvocation, provides useful information about the method invocation context. For example, we can get or change the returned value. In the following example, we will log the number of retrieved customers using the InterceptAfter method:
Kernel.InterceptAfter<CustomerService>(s=>s.GetAllCustomers(), invocation =>logger.DebugFormat("{0} customers retrieved", (IEnumerable<Customer>) invocation.ReturnValue).Count()));
Since the type of ReturnedValue is object, we need to cast it to reach the Count() method. In the following example, we will implement caching for the GetAll() method of the customers repository:
Kernel.InterceptReplace<SqlCustomerRepository>( r => r.GetAll(), invocation => { const string cacheKey = "customers"; if (HttpRuntime.Cache[cacheKey] == null) { invocation.Proceed(); if (invocation.ReturnValue != null) HttpRuntime.Cache[cacheKey] = invocation.ReturnValue; } else invocation.ReturnValue = HttpRuntime.Cache[cacheKey]; });
In this example, we used the InterceptReplace method which can totally replace the functionality of the intercepted method. We used HttpRuntime.Cache for caching the list of Customer objects, which the GetAll() method returns. If the Cache object is empty, we need to call GetAll(), which is the intercepted method and then put the returned value in the cache. In order to call the intercepted method via the interception method (InterceptReplace), we should call the Proceed() method of the invocation object. Then we can get its returned value which is the list of Customer objects from the ReturnValue property of the invocation. If the Cache object is not empty, we just need to set the ReturnValue property to the cached Customer list. In this way, the GetAll() method will not be called.
The important thing to keep in mind is that the type argument provided for interception methods cannot be the type of the abstracted service. It should be the concrete implementation type. That is why we have provided SqlCustomerRepository rather than ICustomerRepository as the type argument for the InterceptReplace method, so the following code wouldn’t work:
Kernel.InterceptReplace<ICustomerRepository>( r => r.GetAll(), invocation =>{ ... });
That is because interception creates a proxy wrapper around the resolved object rather than creating a new implementation of the abstracted service.
You may have noticed that all of the InterceptXxx methods require a type argument. This obliges the application to have a reference to the dependency library which is usually not desirable. We should be able to refer to types using their names so that we can dynamically load dependency assemblies at runtime. In order to do so, we can use the AddMethodInterceptor method. Here is the implementation of the preceding example using the AddMethodInterceptor method:
var repositoryType = Type.GetType( "Northwind.SqlDataAccess.SqlCustomerRepository, Northwind.SqlDataAccess"); Kernel.AddMethodInterceptor(repositoryType.GetMethod("GetAll"), invocation => { ... });
Type Interception
While method interception targets a particular method or property of a given type, type Interception is more generalized and applies to a type or a group of types, and intercepts all of the methods and properties in a single point. In order to create an interceptor, we need to implement the IInterceptor interface. This interface has only one method, which is as follows:
void Intercept( IInvocation invocation );
In the following example, we will implement an exception handling interceptor which can catch the exceptions and hand them over to an exception handler service. It is the same as putting try-catch in all of the methods of the intercepted type:
public class ExceptionInterceptor : IInterceptor { private IExceptionHandlerService exceptionHandlerService; public ExceptionInterceptor(IExceptionHandlerService handlerService) { this.exceptionHandlerService = handlerService; } public void Intercept(IInvocation invocation) { try { invocation.Proceed(); } catch (Exception exception) { exceptionHandlerService.HandleException(exception); } } }
The following code shows how to add the ExceptionInterceptor to our convention so that it applies to all the classes of our application:
Kernel.Bind(x => x.FromAssembliesMatching("Northwind.*") .SelectAllClasses() .BindAllInterfaces() .Configure(b => b.Intercept() .With<ExceptionInterceptor>() ));
The Intercept() method is added to the configuration section of our convention and accepts the type of the desired interceptor as its type parameter. It can then activate the provided type to create and apply the interceptor object.
If we need to intercept only a certain type in a convention rule, we can use the ConfigureFor method:
Kernel.Bind(x => x.FromAssembliesMatching("Northwind.*") .SelectAllClasses() .BindAllInterfaces() .ConfigureFor<ICustomerRepository> (b => b.Intercept() .With<ExceptionInterceptor>() ));
If we already have an instance of our interceptor, we can use the following syntax:
var exceptionInterceptor = Kernel.Get<ExceptionInterceptor>(); Kernel.Bind(x => x.FromAssembliesMatching("Northwind.*") .SelectAllClasses() .BindAllInterfaces() .Configure(b => b.Intercept() .With(exceptionInterceptor ) ));
The preceding example showed how to intercept types projected by a convention. It is also possible to intercept the kernel itself. The following example applies ExceptionInterceptor to all of the services resolved by the kernel, no matter how they are registered:
kernel.Intercept(context => true) .With<ExceptionInterceptor>();
The Intercept method accepts a predicate which is given an instance of the current activation context (IContext). This predicate indicates what services to choose for interception. In this example, we always return true, which means we intend to intercept all services. We can define any contextual condition by this predicate based on the activation context. Refer to the Contextual binding section in Chapter3 for refreshing how to define contextual conditions.
There is also a built-in interceptor class named ActionInterceptor, which can be used as a generic interceptor in case our interception logic is as simple as a single method:
Kernel .Intercept() .With(new ActionInterceptor (invocation => log.Debug(invocation.Request.Method.Name)));
The Interception extension also contains an abstract SimpleInterceptor class, which can be extended, to create interceptors with a pre/post interception logic, and an AutoNotifyPropertyChangedInterceptor class which is designed specifically for WPF ViewModels and automates notification of property changes.
Multiple Interceptors
We already studied how to implement exception handling concern using interception. But what if we need to add more interceptors to a type? In real life scenarios we usually have to implement a variety of cross-cutting concerns on each type. Multiple interception allows us to meet this requirement. The following example shows how to address both logging and exception handling concerns using two interceptors:
kernel.Intercept(context => true).With<ExceptionInterceptor>(); kernel.Intercept(context => true).With<LoggerInterceptor>();
Alternatively, we can apply them to a convention similar to this:
Kernel.Bind(x => x.FromAssembliesMatching("Northwind.*") .SelectAllClasses() .BindAllInterfaces() .Configure(b => { b.Intercept() .With<ExceptionInterceptor>(); b.Intercept() .With<LoggerInterceptor>(); } ));
We can also register multiple interceptors on a single Binding in the same way as follows:
var binding = Bind<IService>().To<MyService>(); binding.Intercept().With<ExceptionInterceptor>(); binding.Intercept().With<LoggerInterceptor>();
When we register an interceptor for a service type, Ninject no longer resolves the service by activating the service itself. Instead, Ninject returns a proxy object which wraps an instance of the service. When we call a method on the resolved object, we are actually calling the proxy implementation of that method, rather than the actual service method. The following diagram demonstrates that the proxy method invokes the Intercept method on the first registered interceptor:
If the Proceed method is called within the Intercept method, the proxy class advances to the next interceptor and executes its Intercept method. Calling the Proceed method in the last interceptor, leads to calling the actual service method. Once the actual service method is called, the control returns to the last Intercept method along with the value returned by the service method. Here is where the interceptor can perform post-invocation operations (for example, modifying the returned value). The control then returns to the previous interceptors one by one, until it reaches the proxy method which was initially called. The following diagram shows this sequence:
When we register multiple interceptors, the order in which they intercept can be indicated using the InOrder method as follows:
Kernel.Bind(x => x.FromAssembliesMatching("Northwind.*") .SelectAllClasses() .BindAllInterfaces() .Configure(b => { b.Intercept() .With<ExceptionInterceptor>() .InOrder(1) ; b.Intercept() .With<LoggerInterceptor>() .InOrder(2) ; } ));
The lower the value of the order, the earlier the interceptor executes. So, in the preceding example, ExceptionInterceptor executes before LoggerInterceptor.
Intercept Attribute
Another way of registering an interceptor for a type or method is by using attributes. In order to create an attribute interceptor, we just need to derive from the InterceptAttribute class and override its CreateInterceptor method. In the following example, we create an attribute named InterceptExceptionsAttribute for intercepting exceptions:
public class InterceptExceptionsAttribute : InterceptAttribute { public override IInterceptor CreateInterceptor(IProxyRequest request) { return request.Kernel.Get<ExceptionInterceptor>(); } }
We can then apply this attribute to a method or a type as follows:
[InterceptExceptions] public class Sample { ... } We can also apply both attributes to the same type as shown in the following code: [InterceptExceptions, Log] public class Sample { ... }
We can apply the interceptor attributes also to methods (remember that in either way, the method should be virtual or it will not be intercepted) as follows:
[InterceptExceptions] public class Sample { [Log] public virtual void DoSomething() { ... } ... }
In the preceding example, all of the virtual methods within the Sample class will be intercepted by ExceptionInterceptor. The DoSomething method is also intercepted by LoggerInterceptor.
We can also specify the order of interceptors, by setting the Order property of the applied attribute as follows:
[InterceptExceptions(Order = 2)] public class Sample { [Log(Order = 1)] public virtual void DoSomething() { ... } }
In the preceding example, the DoSomething method will be intercepted first by LoggerInterceptor and then by ExceptionInterceptor.
In case if we have methods which we don't want to be intercepted, we can exclude them using the [DoNotIntercept] attribute as follows:
[InterceptExceptions] public class Sample { [Log] public virtual void DoSomething() { ... } [DoNotIntercept] public virtual void DoSomethingElse() { ... } }
In the preceding example, although the [InterceptExceptions] attribute is applied to the type, it doesn't intercept the DoSomethingElse method.
Hi Daniel. Having read your book, Mastering Ninject for DI, I am wondering if you have any thoughts on how to test that a method is actually intercepted? I was hoping for some kind of extension method, Maybe somthing like HasInterceptor(x=>x.MyMethod, 1). The first argument is the method that I want to test for interception and the second the interceptor order.
Thanks
Hi jonas,
There are different ways to examine a method for interception. It depends where you want to do that. In a target which is receiving the intercepted object, you can simply inspect the type of the object. But the target should totally be agnostic of wether or not the dependency is intercepted. In the interceptor itself, you can get the list of other interceptors of a method from the invitation object. Let me know if you need more details.
Hi Daniel. I was hoping to do a integrationtest. In my test I would like to call my method and afterwards assert that the method in fact was intercepted. I currently do this in a rather ugly way by adding a boolean property “Called” to my interception class and checking this after I call my method.
[TestMethod]
public void Interceptor_can_be_found_in_kernel()
{
//Arrange
var kernel = new StandardKernel();
//bl
kernel.Bind().To();
kernel.Bind().ToSelf().InSingletonScope();
var ei = kernel.Get();
kernel.InterceptBefore(x => x.SendWithParameter(null, null), ei.Intercept);
//sut
var mybl = kernel.Get();
//act
mybl.SendWithParameter(new DummyContextClass(), new CreditorCoreData());
//Assert
ei.Called.Should().BeTrue();
}
public class ExceptionAspect : IInterceptor
{
public bool Called = false;
public void Intercept(IInvocation invocation)
{
Called = true;
…
So you want to assert whether the intercept method was called or not. In this case a unit test is needed, rather than an integration test. Because an integration test doesn’t care whether a particular method was called. What an integration test asserts is the effect of that method call on the entire system. For example if the intercept method is doing a validation and throws an exception on an invalid argument, then what you assert is whether an exception was actually thrown.
Alternatively if you need to test whether the Intercept method was called, you can mock the interceptor and examine the mock object to verify the method call. You have pretty much taken the second approach except that instead of creating an isolated mock, you contaminated your sut with an assertion property. I would use an isolation framework like Moq.
Appart from the test, you can always reflect an intercepted method to check wether it belongs to your concrete implementation or to a proxy object. Interception always wraps the intercepted object in a proxy.
I have it working with moq. Thanks
Pingback: Ninject. Configurar interceptors – Desarrollo en .NET
Hi, I found this article REALLY useful. Would it be easy to get the images back?
Thanks, Which one is broken?
Hello Daniel, very nice article, it is probably going to help me a lot 🙂
I just have one problem –> my interception only works, when the method is called “from the outside” … so basically, if I have this:
Bind().To().Intercept().With(),
it only works, when I Ninject somewhere the INTERFACE, and I call the Method … but when I am inside the IMPLEMENTATION, and I call the Method from OtherMethod, it is not intercepted 😦
That’s normal because method calls are intercepted when they cross the proxy layer wrapping your object. An internal call initiated from the same object doesn’t cross that layer hence doesn’t reach to interception logic.
A method needing another method within the same class could be a sign that your class is not SOLID and has more than one responsibility. The solution is moving the second logic to another class (S).