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. Now we are going to look at a benefit of having this Mary Poppins bag of magic controlled for us.
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
The time of your life
In our code we created a global variable for the logging and just called that, but that is not always a good idea. Logging is a poor example of this, but security is a better one. If you wanted to do a security check in some methods and not others why waste time with a global variable, why not reach into the bag each time it is needed and pull one out. The problem can come in, is that performance can drop because now you are calling the constructor each time you need the object. The reality is that this issue was solved years ago in the Singleton pattern.
So can we apply that to Unity? Yes, Unity is smart enough to give us a way to do that thanks to it’s inbuilt lifetime management! To demo this I have modified the code from part 2 as follows:
- Removed the global variable for logger and replaced it with a global variable for the container (line 3 below)
- In DoSomething I ask for a logger at the start now (line 7 below)
- I got a Lolzcat to add a constructor to my ConsoleLogger class to slow it down ;) Lines 45 to 49.
- It’s not visible in the code by a new using has been added to System.Threading so that we can use the Thread.Sleep method on line 48
1: class Program
2: {
3: static IUnityContainer container = new UnityContainer();
4:
5: static void DoSomething(string Username)
6: {
7: ILogger logger = container.Resolve<ILogger>();
8:
9: logger.LogThis("DoSomething Called with Username Parameter set to:" + Username);
10: Console.WriteLine("Hello {0}", Username);
11: logger.LogThis("DoSomething Completed");
12: }
13:
14: static void Main(string[] args)
15: {
16: UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
17: section.Containers.Default.Configure(container);
18:
19: ILogger logger = container.Resolve<ILogger>();
20:
21: logger.LogThis("Application started");
22:
23:
24: DoSomething("Robert");
25: Console.ReadKey();
26: logger.LogThis("Application Completed");
27: }
28: }
29:
30: public interface ILogger
31: {
32: void LogThis(string Message);
33: }
34:
35: public class DebugLogger : ILogger
36: {
37: public void LogThis(string Message)
38: {
39: System.Diagnostics.Debug.WriteLine(String.Format("{0}: {1}", DateTime.Now, Message));
40: }
41: }
42:
43: public class ConsoleLogger : ILogger
44: {
45: public ConsoleLogger()
46: {
47: Console.WriteLine("i iz in yourz codez, slowingz its downz");
48: Thread.Sleep(5000);
49: }
50:
51: public void LogThis(string Message)
52: {
53: Console.WriteLine("{0}: {1}", DateTime.Now, Message);
54: }
55: }
What happens if my code runs slowly now and the lolzcat message is shown twice (since I create the object twice). If I assume that it’s just an example of the overhead of the constructor (which it is), and that I need to live with it at least once then implementing the singleton pattern is the solution to this. To do it actually means two lines of changes to the app.config (one line if you want to be messy). First I add a new type alias for the ContainerControlledLifetimeManager which I have called singleton (line 11 below), and then I expand my type with a lifetime tag which links back to it (line 18 below).
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <configSections>
4: <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
5: </configSections>
6:
7: <unity>
8: <typeAliases>
9: <typeAlias alias="Logger" type="BigSystem.ConsoleLogger, BigSystem"/>
10: <typeAlias alias="ILogger" type="BigSystem.ILogger, BigSystem"/>
11: <typeAlias alias="singleton" type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager, Microsoft.Practices.Unity" />
12: </typeAliases>
13:
14: <containers>
15: <container>
16: <types>
17: <type type="ILogger" mapTo="Logger">
18: <lifetime type="singleton" />
19: </type>
20: </types>
21: </container>
22: </containers>
23: </unity>
24: </configuration>
Now I just rerun my application and I get the code twice as fast and one lolzcat message since the container (magic bag) has now created the object once and when I ask for it a second time it provides me the already created one! This is an amazingly useful system because it’s per mapping and out of the box are three lifetime managers and you can build your own.
For the sake of ease I will now copy and paste from the documentation on those three lifetime managers:
ContainerControlledLifetimeManager. Unity returns the same instance of the registered type or object each time you call the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes. This lifetime manager effectively implements a singleton behavior for objects. Unity uses this lifetime manager by default for the RegisterInstance method if you do not specify a different lifetime manager. If you want singleton behavior for an object that Unity creates when you use the RegisterType method, you must explicitly specify this lifetime manager. The behavior is as follows:
- If you used the RegisterType method to register a type, Unity creates a new instance of the registered type during the first call to the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes. Subsequent requests return the same instance.
- If you used the RegisterInstance method to register an existing object, Unity returns this instance every time you call the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes.
ExternallyControlledLifetimeManager. This lifetime manager allows you to register type mappings and existing objects with the container so that it maintains only a weak reference to the objects it creates when you call the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes based on attributes or constructor parameters within that class. Unity returns the same instance of the registered type or object each time you call the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes. However, the container does not hold onto a strong reference to the object after it creates it, which means that the garbage collector can dispose of the object if no other code is holding a strong reference to it.
PerThreadLifetimeManager. Unity returns, on a per-thread basis, the same instance of the registered type or object each time you call the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes. This lifetime manager effectively implements a singleton behavior for objects on a per-thread basis. PerThreadLifetimeManager returns different objects from the container for each thread. The behavior is as follows:
- If you used the RegisterType method to register a type, Unity creates a new instance of the registered type the first time the type is resolved in a specified thread, either to answer a call to the Resolve or ResolveAll methods for the registered type or to fulfill a dependency while resolving a different type. Subsequent resolutions on the same thread return the same instance.
- Using the RegisterInstance method to register an existing object results in the same behavior as if you just registered the lifetime container with RegisterType. Therefore, it is recommended that you do not use the RegisterInstance method to register an existing object when using the PerThreadLifetimeManager.
- PerThreadLifetimeManager returns the object desired or permits the container to create a new instance if no such object is currently stored for the current thread. A new instance is also created if called on a different thread than the one that set the value. This lifetime manager does not dispose the instances it holds.