Although I have not written the final part of my BizTalk Fault Message support posts, I recently came across an issue that I thought needed to be shared. I'm currently working on a medium-to-large project using NHibernate for data access. One of the pillars of NHibernate development is session management. Our data access is done across WCF services, using Unity for injecting the NHibernate sessions into our repositories. Naively, our sessions were using the per-thread lifetime manager included with Unity. This seemed to work great to select our data. Unfortunately, when we began to update our data, a very strange issue would show up occasionally.
My developers began to tell me that every so often, a NonUniqueObjectException would be thrown when they tried to update a given entity. The problem was that they could never reproduce it, nor were the steps the same every time. I put this issue on the back burner so I could finish building out a service operation, when all of a sudden, I got the same error.
In my case, I was selecting a record with the first service call, and updating the same record with my second service call. This simple set of service calls surfaced the issue, and it was reproducible about 80% of the time. After spending a couple of minutes looking at the stack trace and generally debugging the issue, it finally dawned on me what was happening. WCF's threading model, which was designed to make my services speedy, was actually hurting me in this case.
I don't know the exact details of how (for a good explanation of WCF IOCP threading internals, go here), but in a nutshell, the same thread that was obtained in my first service call was being used to service my second service call. Because I was using the per-thread lifetime manager in Unity, the NHibernate session was not being cleaned out until the thread goes away. Well, in WCF, the threads don't really go away. Uh oh!
After conferring with Scott, we came up with the idea to build a Unity lifetime manager tied to WCF's OperationContext. That way, our NHibernate session would live only for the life of the request, rather than the thread.
Starting with IExtension<OperationContext> and an implementation of IDispatchMessageInspector, I soon had an implementation of a UnityOperationContextLifetimeManager. This is the one we ended up using to support our NHibernate sessions. Once we used this lifetime manager instead of the per-thread lifetime manager, the NonUniqueObjectExceptions disappeared.
Once that was done, I figured since I did one of the extensible objects in WCF, I might as well do the rest. The attached zip has lifetime managers to support all of the IExtensibleObject<T> implementations within WCF.
All of the lifetime managers are fairly straightforward, except for the UnityInstanceContextLifetimeManager. This depends on the InstanceContext of a given service on which it is enabled. PerSession is the default instancing mode for InstanceContext, but only when the transport supports sessions. If the transport does not support sessions, the instancing mode is essentially PerCall.
Please note that the testing application is not necessarily a best practice for self-hosting a service, but I wanted you to see how to use the lifetime managers without having to worry about IIS (since IIS 6 doesn't have WAS and doesn't support the net.tcp binding). Make sure if you are on Vista, you run this sample application as admin.
We're doing something similar on my current project with Unity and Linq to SQL.
ReplyDeleteThe approach we took was to make an implementation of IInstanceProvider. The IInstanceProvider holds a static Unity container which includes all the type dependencies. When GetInstance is called it creates a child container and registers instance dependencies with it. The service instance is pulled from this container and then the container is stashed on the instance context. When ReleaseInstance is called we do some cleanup and dispose the child container. The IInstanceProvider gets hooked up by a IContractBehaviorAttribute.
IMO, this is a much simpler approach. It's certainly less code.
Sorry. I sent that last post before I took a good look at the download. It does a heap more than our simple solution and it's actually pretty awesome. Please upload this to CodePlex.
ReplyDeleteI second the request to upload this to CodePlex. Along with my kudos on this...and the only word that seems fitting......Brilliant !
ReplyDeleteGreat example. I tried using the UnityInstanceContextLifetimeManager and found that dispose wasn't being called on the objects registered. The fix was to subscribe to the Closing event on the InstanceContext in the UnityInstanceContextExtension and call Detach on the current UnityInstanceContextExtension instance.
ReplyDeleteIs there a better way?
This makes sense when you host the services yourself, but, how would you transform this sample to use it when the services are hosted in IIS?
ReplyDeleteThis is just perfect.
ReplyDeleteI have had a similar problem using Telerik OpenAccess, but in essense the problem is the exact same. I was using ObjectBuilder and a specific strategy to handle this, because no Unity existed at that moment in time... but this.. It's brilliant as Marcelo already said.
You made my day. Awesome code.
ReplyDeleteI just had to change some details within UnityInstanceProvider and now everything works perfectly in my environment.
You should definitly publish this on CodePlex.
Hi, I'm implementing my own IINstanceProvider class but the Release() method never gets called? It's implemented using an IServiceBehavior and attached to the exposed endpoints. No matter how hard we hammer the service the Relaease() method nevers gets called. We can the service running a per call instanceContext mode with 50 instance max. The deconstruct of the instances gets called but this looks like the gargageCollection rather than wcf realeasing and disposing. An ideas?
ReplyDeleteSorry all for the lack of responses to the comments. Work has been insane. I will try to answer the questions in order.
ReplyDelete@Anonymous - Good catch. Your solution should work fine. However, I believe the "correct" approach, judging by the documentation, is to detach the extension in the IInstanceProvider.ReleaseInstance() method. You should be able to find the IExtension<> and call detach. I will try to make this change and update soon.
@Roberto - The self-hosted service was simply for a simple, sample application. We are using the exact same code in an IIS hosted service today. It works exactly the same.
@Jon - The IInstanceProvider.ReleaseInstance() isn't being called? What binding are you using for your service? As far as I know, this should work.
ReplyDelete