Interception with Ninject

Featured

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.

6207_05_01

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:
6207_05_02

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:
6207_05_03

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.

Change The Author of Git Commits

  1. Identify one revision before the commit you need to update. In the following example, the last two commits are going to be updated.
    CommitLogs
  2. Run git rebase command with the identified commit number:
    git rebase -i 2778751
  3. In the editor, for each commit you need to update, change the word pick to edit. (Make sure you know how to use the Vim editor)
  4. Run the following command to set the author to the current configured git user:
    git commit --amend --reset-author --no-edit
    Or use the following to set it to a custom author:
    git commit --amend --no-edit --author="Author Name <email@address.com>"
  5. Run git rebase --continue
  6. Repeat step 4 and 5 until all of the commits are updated.

Install .NET Core on Ubuntu 15.10

Currently .NET Core is only supported for Ubuntu 14.04 and when you try installing it on Ubuntu 15.10 you get the following error:

Some packages could not be installed. This may mean that you have
requested an impossible situation or if you are using the unstable
distribution that some required packages have not yet been created
or been moved out of Incoming.
The following information may help to resolve the situation:

The following packages have unmet dependencies:
 dotnet : Depends: libicu52 (>= 52~m1-1~) but it is not installable
E: Unable to correct problems, you have held broken packages.

As a workaround you can download and install libicu52 manually before installing dotnet .

How big can a class be

A class should be so small that anyone can easily understand it. If a method is not required to be understood in order to understand the class, then that method probably doesn’t belong in there and it’s not part of the responsibility of that class.

It is not a good idea to judge how big a class is based on the number of lines of code, however the following list can give you a rough estimate:

  • Less than 100: Ideal
  • 100 to 200: Ok
  • 200 to 300: Warning
  • 300+: The class is too big

How to install JSon.Net NuGet package

Problem:

You are going to install Json.NET NuGet package but you get the following error:

 JSON Failed to initialize the PowerShell host. If your PowerShell execution policy setting is set to AllSigned, open the Package Manager Console to initialize the host first.

Solution:

  1. Open PowerShell console in Administrator mode. Note that you should open the x86 version if you are running the 32bit version of Visual Studio. Likewise open x64 if your Visual Studio is 64bit.
  2. Run this command start-job { Set-ExecutionPolicy Unrestricted } -RunAs32 | wait-job | Receive-Job
  3. Reopen Visual and the issue should be fixed