Recap + Intro
In part 1 we looked at the problem with just adding logging code and how it quickly gets messy and we looked at 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 which gave us control via a config file as to what class our logging should use. Then in part 3 we saw some benefits of dealing with this overhead. Previously we actually got our logging nicely sorted but there was a few extra details worth mentioning, so here goes.
Note you can get Unity with the Enterprise Library from 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 as people smarter than me have done a much better job. If you want the theory look somewhere else, if you want to get running with Unity this is for you.
This is a multi-part series as such here is the series guide in case you are looking for the rest of the series:
- Part 1 - Introduction to the problem
- Part 2 - Changing the code to use basic unity functions
- Part 3 - Life time management
- Part 4 - Changing the code to use interception
- Part 5 - Interception supplementary
- Part 6 - Wrap up
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 responsibility of the developer to prevent this type of error by ensuring that the members of classes they use with dependency injection do not contain circular references.
I have bolded that last line to get the point across. You have been warned!
Reflection
It’s not just interception that uses reflection, other parts do as well so make sure you understand the security implications of this: http://msdn.microsoft.com/en-us/library/9syytdak.aspx
Interceptors
I used the transparent proxy interceptor in my previous post, but there are two others. Each has pro’s and con’s, which the documentation puts in a simple table for you:
Type
Description
Use
TransparentProxyInterceptor
An instance interceptor. The proxy is created by 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.
InterfaceInterceptor
An instance interceptor. It can proxy only one interface on the object. It uses dynamic code generation to create the proxy class.
When resolving an interface mapped to a type.
VirtualMethodInterceptor
A 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 specific needs, because each one has various tradeoffs. The following table summarizes the three interceptors and their advantages and disadvantages.
Type
Advantages
Disadvantages
TransparentProxyInterceptor
Can 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.
InterfaceInterceptor
Proxy supports casting to all the interfaces or types of the target object.
It only intercepts methods on a single interface. It cannot cast a proxy back to target object's class or to other interfaces on the target object.
VirtualMethodInterceptor
Calls 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 which lets you enable/disable the interception based on rules. The idea is that you annotate all methods and then use the rules to filter which of those to actually run. This can, as with everything in Unity, be configured in code or in the configuration file. Here are a list of the rules (I am sure you can begin to guess what they do by their names):
- The Assembly Matching Rule
- The Custom Attribute Matching Rule
- The Member Name Matching Rule
- The Method Signature Matching Rule
- The Namespace Matching Rule
- The Parameter Type Matching Rule
- The Property Matching Rule
- The Return Type Matching Rule
- The Tag Attribute Matching Rule
- The Type Matching Rule
And as always you can custom develop your own rules too.