I really love DI-frameworks. One reason is that it allows me to centralize life-time management of objects and services needed by others. Most frameworks have options to control the lifetime and allows objects to be created as Singletons, ThreadStatics, Transients (a new object everytime) etc.
I’m currently doing some work on a project where Unity is the preferred DI-framework and it’s hooked to resolve all service objects for us.
The challenge
WCF has three options for instances, PerCall, PerSession and Single (http://msdn.microsoft.com/en-us/library/system.servicemodel.instancecontextmode.aspx). IN essence this will have the following effect on dependencies injected:
PerCall – All dependencies will be resolved once per message sent to the service.
PerSession – All dependencies will be resolved once per WCF service Session.
Single – All dependencies will be resolved exactly once (Singleton).
Now why is this challenge? It’s as expected that the constructor parameters is called once per instantiation, isn’t it?
The basis of the challenge is when you want to share instances of dependencies one or more levels down. Consider this code: ´
1: public class CalculationService : Contract {
2: public CalculationService(IRepositoryruleRepository,
3: IRepositoryproductRepository){..}
4: }
This would work perfectly fine with the InstanceContextModes explained above, but what if we want to share instances of NHibernate Sessions or Entity Framework Contexts between repositories?
The default setting for most DI-Frameworks is to always resolve objects as “Transients”, which means once for each object that is dependent on them.
This is where LifeTime management comes into play by changing how the DI-Framework shares instances between dependant objects.
Unity has six “sharing-options” (from http://msdn.microsoft.com/en-us/library/ff660872(PandP.20).aspx):
TransientLifetimeManager: | A new object per dependency |
ContainerControlledLifetimeManager | One object per unity container instance (including childrens) |
HierarchicalLifetimeManager | One object per unity child container |
PerResolveLifetimeManager | A new object per call to resolve, recursive dependencies will reuse objects. |
PerThreadLifetimeManager | One new object per thread |
ExternallyControlledLifetimeManager | Moves the control outside of Unity |
As you can see we are missing our scenario. We’d like to share all dependencies of some objects across a single service instance, no matter what InstanceContextMode we choose.
The Solution
For Unity there is a good extension point that can help us. Combine that with WCF’s ability to add extensions to Instances and problem will be solved.
First we extend WCF instance context so it can hold objects created by Unity:
1: public class WcfServiceInstanceExtension : IExtension
2: {
3: public static WcfServiceInstanceExtension Current
4: {
5: get
6: {
7: if (OperationContext.Current == null)
8: return null;
9:
10: var instanceContext = OperationContext.Current.InstanceContext;
11: return GetExtensionFrom(instanceContext);
12: }
13: }
14:
15: public static WcfServiceInstanceExtension GetExtensionFrom(
16: InstanceContext instanceContext)
17: {
18: lock (instanceContext)
19: {
20: var extension = instanceContext.Extensions
21: .Find();
22:
23: if (extension == null
24: {
25: extension = new WcfServiceInstanceExtension();
26: extension.Items.Hook(instanceContext);
27:
28: instanceContext.Extensions.Add(extension);
29: }
30:
31: return extension;
32: }
33: }
34:
35: public InstanceItems Items = new InstanceItems();
36:
37: public void Attach(InstanceContext owner)
38: { }
39:
40: public void Detach(InstanceContext owner)
41: { }
42: }
InstanceContextExtension, which get’s applied on each WCF Service Instance
1: public class InstanceItems
2: {
3: public object Find(object key)
4: {
5: if (Items.ContainsKey(key))
6: return Items[key];
7:
8: return null;
9: }
10:
11: public void Set(object key, object value)
12: {
13: Items[key] = value;
14: }
15:
16: public void Remove(object key)
17: {
18: Items.Remove(key);
19: }
20:
21: private Dictionary<object, object> Items
22: = new Dictionary<object, object>();
23:
24: public void CleanUp(object sender, EventArgs e)
25: {
26: foreach (var item in Items.Select(item => item.Value))
27: {
28: if (item is IDisposable)
29: ((IDisposable)item).Dispose();
30: }
31: }
32:
33: internal void Hook(InstanceContext instanceContext)
34: {
35: instanceContext.Closed += CleanUp;
36: instanceContext.Faulted += CleanUp;
37: }
38: }
InstanceItems, used by the extension to hold objects created by unity
This gives us a nice place to put created objects, it will also call dispose on any objects when the instance is closing down.
Now we need to tell Unity to use our new shiny class, this is done by first extending LifeTimeManager:
1: public class WcfServiceInstanceLifeTimeManager : LifetimeManager
2: {
3: private readonly Guid key;
4:
5: public WcfServiceInstanceLifeTimeManager()
6: {
7: key = Guid.NewGuid();
8: }
9:
10: public override object GetValue()
11: {
12: return WcfServiceInstanceExtension.Current.Items.Find(key);
13: }
14:
15: public override void SetValue(object newValue)
16: {
17: WcfServiceInstanceExtension.Current.Items.Set(key, newValue);
18: }
19:
20: public override void RemoveValue()
21: {
22: WcfServiceInstanceExtension.Current.Items.Remove(key);
23: }
24: }
The LifeTimeManager that uses our WCF Extension
All that’s left now is to tell Unity when to use this LifeTimeManager instead of the default. That is done when we register the type:
container.RegisterType(new WcfServiceInstanceLifeTimeManager(), new InjectionFactory( c => SessionFactory.CreateSession()));
In conclusion
So, DI-Frameworks are powerful to handle dependencies but sometimes they need a little nudge in the right direct. Custom LifeTimeManagement is one of those nudges you can do and both Unity and WCF helps you do that.
I did this for StructureMap a while back so reading this gave me tons of deja vu moments
I blogged about it here:
http://andreasohlund.blogspot.com/search/label/WCF
I’ll guess you’ll need some kind of UoW implementation as well?
It’s surely an interesting idea to set up extension for UoW handling.
In the particular scenario I’m in, UoW is completly handled by a Transaction scope and NHibernate.
I’m using FlushMode.Commit and WCF AutoComit with transactionscopes for any operations that execute anything against the database.
Thus the need for a custom UoW handler is actually obsolete
True!
Just make sure that you don’t leak any connections though:
http://davybrion.com/blog/2010/05/avoiding-leaking-connections-with-nhibernate-and-transactionscope/
So how do you manage the Nhibernate Sessions, for instance closing, flushing and disposing?
You’ll do that in the CleanUp method. There is several ways.
Can you provide a simple example? I have used a session per request in MVC, but kind of stuck since I dont know how it works with WCF. Where does the CleanUp Method reside, how do you make sure that is called when Unity is disposing of the Session?
Thank you,
thank you very much.
you solution solve my problem.
多谢,十分感谢。祝你快乐