Using Autofac DynamicProxy2 Interceptors with MVC3–Part 1
Autofac is an IoC container and using DynamicProxy we can intercept method calls to perform custom logic. What this means is that, you can actually add ‘hooks’ to methods in your code so that when these methods are called, you automatically invoke an ‘interceptor’ method that wraps the call to your method.
This comes in handy when say, you want some custom business logic to run for all methods that are defined in an interface. Instead of adding your custom logic in each and every method, you can have it in one neat place and re-use it for all the methods.
Here’s how I’m going to implement this:
- An MVC application will call a WCF service
- All service calls are logged as ‘Information’ entries
- If the service does not respond within a specified amount of time, we create a ‘Warning’ entry mentioning the methods name
- If the service throws an exception, we write an ‘Error’ entry giving the details of the exception
For this, I have created my solution to have two projects:
- ServiceApplication is the one that holds the WCF service
- AutofacInterceptor is the UI piece of the puzzle that consumes and calls the above service
This is what my WCF service actually does:
1: public class SomeRandomService : ISomeRandomService
2: {
3: public int RunsJustFine()
4: {
5: return 1;
6: }
7:
8: public int ThrowsException()
9: {
10: int i = 3;
11: i = i / (i - 3);
12: return i;
13: }
14:
15:
16: public int UnderPerforms()
17: {
18: Thread.Sleep(2000);
19: return 2;
20: }
21: }
In my UI project, I have a proxy class that calls the service methods. I’ll use the ISomeRandomServiceProxy interface as a hook to intercept the methods, RunsJustFine(), ThrowsException() and UnderPerforms() declared in the SomeRandomServiceProxy class.
1: public interface ISomeRandomServiceProxy
2: {
3: int RunsJustFine();
4:
5: int ThrowsException();
6:
7: int UnderPerforms();
8: }
9:
10: public class SomeRandomServiceProxy : ISomeRandomServiceProxy
11: {
12: private readonly SomeRandomServiceClient _someRandomServiceClient =
13: new SomeRandomServiceClient("BasicHttpBinding_ISomeRandomService");
14:
15: public int RunsJustFine()
16: {
17: return _someRandomServiceClient.RunsJustFine();
18: }
19:
20: public int ThrowsException()
21: {
22: return _someRandomServiceClient.ThrowsException();
23: }
24:
25: public int UnderPerforms()
26: {
27: return _someRandomServiceClient.UnderPerforms();
28: }
29: }
I can create an instance of the SomeRandomServiceProxy class in my controller to call all the methods and things would work just fine. But that’s not what we are here for, now is it?
Let’s install the assemblies that are needed using NuGet packages through Package Manager Console.
PM> install-package autofac
Successfully installed 'Autofac 2.5.2.830'.
Successfully added 'Autofac 2.5.2.830' to AutofacInterceptor.
PM> install-package castle.core
Successfully installed 'Castle.Core 2.5.2'.
Successfully added 'Castle.Core 2.5.2' to AutofacInterceptor.
PM> install-package autofaccontrib.dynamicproxy2
Attempting to resolve dependency 'Autofac (= 2.4.5.724)'.
Attempting to resolve dependency 'Castle.Core (= 2.5.2)'.
Attempting to resolve dependency 'Castle.DynamicProxy (= 2.2.0)'.
Attempting to resolve dependency 'Castle.Core (= 1.2.0)'.
Install-Package : Updating 'Castle.Core 2.5.2' to 'Castle.Core 1.2.0' failed. Unable to find a version of 'AutofacContrib.DynamicProxy2' that is compatible with 'Castle.Core 1.2.0'.
At line:1 char:16
+ install-package <<<< autofaccontrib.dynamicproxy2
+ CategoryInfo : NotSpecified: (:) [Install-Package], InvalidOperationException
+ FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PowerShell.Commands.InstallPackageCommand
PM> install-package autofaccontrib.dynamicproxy2 -ignoredependencies
Successfully installed 'AutofacContrib.DynamicProxy2 2.4.5.724'.
Successfully added 'AutofacContrib.DynamicProxy2 2.4.5.724' to AutofacInterceptor.
PM> install-package autofac.mvc3 Attempting to resolve dependency 'Autofac (≥ 2.5.2.830)'. Successfully installed 'Autofac.Mvc3 2.5.2.830'. Successfully added 'Autofac.Mvc3 2.5.2.830' to AutofacInterceptor.
The four packages that we need are: autofac, castle.core, autofaccontrib.dynamicproxy2 and the autofac.mvc3. Since there’s a versioning issue when trying to install the autofac.dynamicproxy2, I have installed ignoring the dependencies.
Below is my SomeRandomServiceInterceptor class that wraps around all the three methods.
1: public class SomeRandomServiceInterceptor : IInterceptor
2: {
3: // if the service does not respond by 500ms,
4: // it's time to fine-tune the service method;
5: private const int ServiceToRespondBy = 500;
6:
7: public void Intercept(IInvocation invocation)
8: {
9: // intercepts the proxy's method call
10: // allows all this logic to be defined only in one place
11: try
12: {
13: // write what method is being called
14: WriteEvent(string.Format("Calling method {0}", invocation.Method.Name), EventLogEntryType.Information);
15: Stopwatch stopwatch = new Stopwatch();
16: stopwatch.Start();
17: // continues the call to the proxy's method
18: invocation.Proceed();
19: stopwatch.Stop();
20: if (stopwatch.ElapsedMilliseconds > ServiceToRespondBy)
21: {
22: WriteEvent(string.Format("Method {0} is underperforming", invocation.Method.Name),
23: EventLogEntryType.Warning);
24: }
25: }
26:
27: catch (Exception exception)
28: {
29: WriteEvent(string.Format("Message: {0}, Source: {1}, Stack: {2}",
30: exception.Message,
31: exception.Source,
32: exception.StackTrace),
33: EventLogEntryType.Error);
34: throw;
35: }
36: }
37:
38: private static void WriteEvent(string message, EventLogEntryType eventLogEntryType)
39: {
40: EventLog log = new EventLog { Source = "Autofac Log", };
41:
42: try
43: {
44: // checks if the event source is registered
45: if (!EventLog.SourceExists("Autofac Log"))
46: {
47: EventLog.CreateEventSource("Autofac Log", "Application");
48: }
49:
50: //log the details of the error occured
51: log.WriteEntry(message, eventLogEntryType);
52: }
53: catch (Exception)
54: {
55:
56: }
57: finally
58: {
59: log.Dispose();
60: }
61: }
62: }
The first thing to notice is that our class implements the IInterceptor interface which is a member of Castle.DynamicProxy namespace. The Intercept() method is the one that needs to be implemented on our class. This is where we can add common business logic for all the method calls of ISomeRandomServiceProxy interface.
Castle also provides us the IInvocation interface which is a parameter to the Intercept method. It’s instance carries the method name that will get called from the Proceed() method.
Let’s see how we wire-up the interceptor to make us do what we want it to do in the next post. Sorry, for leaving your all high and dry, but I’m avoiding writing looooong blogs.