How and where to store nhibernate session in winforms per request
Background
I've read all kinds of blogs and documentation about nhibernate session management. My issue, is I need it for both winforms and webforms. That's right, I'm using the same data layer in both a winforms (windows .exe) and webforms (asp.net web) application. I've read a little about the unit of work pattern and is a good choice for winforms. Storing the nhibernate session in HttpRequest.Current.Items seems like a good way to go for web apps. But what about a combo deal? I have web apps, windows apps, and WCF services that all need to use the same nhibernate data layer. So back to my question...
I plan on using this design: NhibernateBestPractices in my web app like so:
private ISession ThreadSession {
get {
if (IsInWebContext()) {
return (ISession)HttpContext.Current.Items[SESSION_KEY];
}
else {
return (ISession)CallContext.GetData(SESSION_KEY);
}
}
set {
if (IsInWebContext()) {
HttpContext.Current.Items[SESSION_KEY] = value;
}
else {
CallContext.SetData(SESSION_KEY, value); // PROBLEM LINE HERE!!!
}
}
}
The Problem
The problem I am having when using this code in my windows app, is with the line
CallContext.SetData(SESSION_KEY, value);
If I understand CallContext() right, this will keep the session open the entire lifetime of my windows app because it stores the nhibernate session as part of the main applications thread. And I've heard all kinds of bad things about keeping an nhibernate session open for too long and I know by design, it's not mean to stay open very long. If all my assumptions are correct, then the above line of code is a no,no.
Given all this, I'd like to replace the above line with something that will destroy the开发者_Python百科 nhibernate session more frequently in a windows app. Something similar to the lifetime of the HttpRequest. I don't want to leave it up to the windows client to know about the nhibernate session (or transaction) and when to open and close it. I'd like this to be triggered automagically.
The Question
So, where can I store the nhibernate session in a windows app that will allow me (ie. something besides the client) to automatically begin and end a transaction on a per database request (that is, whenever a client makes a call to the DB)?
** Update **
Here are 2 more links on how to implement the unit of work pattern
http://msdn.microsoft.com/en-us/magazine/dd882510.aspx
http://www.codeinsanity.com/2008/09/unit-of-work-pattern.html
Each of your apps can provide a common implementation of an interface like IUnitOfWorkStorage
public interface IUnitOfWorkStorage
{
void StoreUnitOfWork(IUnitOfWork uow);
}
IUnitOfWork can be a wrapper around the ISession which can look like this
public interface IUnitOfWork
{
void Begin();
void End();
}
Begin
might open the session and start a transaction, while End
would commit the transaction and close the session. So you can have 2 implementations of IUnitOfWorkStorage
, one for the WebApp and one for the Windows App. The web app can use HttpContext.Current or something and your windows app can provide just a simple object store that is disposed at the end of your action which would End the UnitOfWork.
I ended up using the following code. The only "burden" it put on my app was the unit tests, and I'd rather muck up that code with session specific info that the production code. I kept the same code as mentioned in my question and then added this class to my unit test project:
namespace MyUnitTests
{
/// <summary>
/// Simulates the IHttpModule class but for windows apps.
/// There's no need to call BeginSession() and EndSession()
/// if you wrap the object in a "using" statement.
/// </summary>
public class NhibernateSessionModule : IDisposable
{
public NhibernateSessionModule()
{
NHibernateSessionManager.Instance.BeginTransaction();
}
public void BeginSession()
{
NHibernateSessionManager.Instance.BeginTransaction();
}
public void EndSession()
{
NHibernateSessionManager.Instance.CommitTransaction();
NHibernateSessionManager.Instance.CloseSession();
}
public void RollBackSession()
{
NHibernateSessionManager.Instance.RollbackTransaction();
}
#region Implementation of IDisposable
public void Dispose()
{
// if an Exception was NOT thrown then commit the transaction
if (Marshal.GetExceptionCode() == 0)
{
NHibernateSessionManager.Instance.CommitTransaction();
}
else
{
NHibernateSessionManager.Instance.RollbackTransaction();
}
CloseSession();
}
#endregion
}
}
And to use the above class you'd do something like this:
[Test]
public void GetByIdTest()
{
// begins an nhibernate session and transaction
using (new NhibernateSessionModule())
{
IMyCustomer myCust = MyCustomerDao.GetById(123);
Assert.IsNotNull(myCust);
} // ends the nhibernate transaction AND the session
}
Note: If you're using this method make to sure to not wrap your sessions in "using" statements when executing queries from your Dao classes like in this post. Because you're managing sessions yourself and keeping them open a littler longer that a single session per query, then you need to get rid of all the places you are closing the session and let the NhibernateSessionModule do that for you (web apps or windows apps).
精彩评论