Tripping over logs: A story of Unity - Part 5

Recap + Intro

In part 1, we looked at the problem with just adding logging code and how it quickly gets messy. We also saw how the more loosely coupled the code is, the easier it is to effect changes painlessly. Then in part 2, we took that code and added Unity, giving us control via a config file as to what class our logging should use. In part 3, we saw some benefits of dealing with this overhead. Previously, we actually got our logging nicely sorted, but there were a few extra details worth mentioning, so here goes.

Note: You can get Unity with the Enterprise Library from the p&p group.

As I said in part 1:

What I am going to do is look at a practical approach to using Unity. I will not be going into DI or IoC—those topics have been covered far better by smarter people than me. If you want the theory, look elsewhere; if you want to get running with Unity, this is for you.

This is a multi-part series, so here’s the series guide in case you’re looking for the rest:

Circular References

DI is actually a minefield, since you can cause a circular reference easily, but injection makes that even easier to shoot yourself in the foot. What is a circular reference? The documentation (shockingly) describes it well:

  • Objects generated through constructor injection that reference each other in their constructor parameters
  • Objects generated through constructor injection where an instance of a class is passed as a parameter to its own constructor
  • Objects generated through method call injection that reference each other
  • Objects generated through property (setter) injection that reference each other

For example, the following code shows two classes that reference each other in their constructors:

public class Class1
{
    public Class1(Class2 test2) { ... }
}

public class Class2
{
    public Class2(Class1 test1) { ... }
}

It is the developer’s responsibility to prevent this type of error by ensuring that the members of classes they use with dependency injection do not contain circular references.

I’ve bolded that last line to emphasize the point. You’ve been warned!

Reflection

It’s not just interception that uses reflection—other parts do as well, so make sure you understand the security implications: http://msdn.microsoft.com/en-us/library/9syytdak.aspx

Interceptors

I used the TransparentProxyInterceptor in my previous post, but there are two others. Each has pros and cons, which the documentation summarizes in a simple table:

TypeDescriptionUse
TransparentProxyInterceptorAn instance interceptor. The proxy is created using the .NET TransparentProxy/RealProxy infrastructure.When the type to intercept is a MarshalByRefObject or when only methods from the type's implemented interfaces need to be intercepted.
InterfaceInterceptorAn instance interceptor. It can proxy only one interface on the object and uses dynamic code generation to create the proxy class.When resolving an interface mapped to a type.
VirtualMethodInterceptorA type interceptor. It uses dynamic code generation to create a derived class that gets instantiated instead of the original, intercepted class and to hook up the call handlers.When only virtual methods need to be intercepted.

Selection of a specific interceptor depends on your needs, as each has trade-offs. The following table summarizes the three interceptors and their advantages and disadvantages:

TypeAdvantagesDisadvantages
TransparentProxyInterceptorCan intercept all methods of the target object (virtual, non-virtual, or interface).The object must either implement an interface or inherit from System.MarshalByRefObject. If the marshal-by-reference object is not a base class, you can only proxy interface methods. The TransparentProxy process is much slower than a regular method call.
InterfaceInterceptorProxy supports casting to all interfaces or types of the target object.It only intercepts methods on a single interface. It cannot cast a proxy back to the target object’s class or to other interfaces on the target object.
VirtualMethodInterceptorCalls are much faster than the TransparentProxyInterceptor.Interception only happens on virtual methods. You must set up interception at object creation time and cannot intercept an existing object.

Rules

There is an entire rules framework available in interception, letting you enable/disable interception based on rules. The idea is that you annotate methods and then use the rules to filter which ones to actually run. As with everything in Unity, this can be configured in code or in the configuration file. Here’s a list of the rules (you can probably guess what they do by their names):

And, as always, you can develop your own custom rules too.