每个请求在winforms中存储nhibernate会话的方式和位置

时间:2011-01-28 23:45:15

标签: asp.net nhibernate session-management

背景

我已经阅读了有关nhibernate会话管理的各种博客和文档。我的问题是,我需要winforms和webforms。没错,我在winforms(windows .exe)和webforms(asp.net web)应用程序中使用相同的数据层。我已经阅读了一些关于unit of work模式的内容,对于winforms来说是一个不错的选择。在HttpRequest.Current.Items中存储nhibernate会话似乎是一个很好的Web应用程序。但是组合交易怎么样?我有网络应用程序,Windows应用程序和WCF服务都需要使用相同的nhibernate数据层。所以回到我的问题......

我计划在我的网络应用中使用此设计:NhibernateBestPractices,如下所示:

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!!!
        }
    }
}

问题

我在Windows应用程序中使用此代码时遇到的问题是使用

CallContext.SetData(SESSION_KEY, value);

如果我理解CallContext(),这会使会话打开我的Windows应用程序的整个生命周期,因为它将nhibernate会话存储为主应用程序线程的一部分。而且我听说过关于让nhibernate会话开放时间过长的各种不好的事情,我知道在设计上,并不是要长时间保持开放。如果我的所有假设都是正确的,那么上面的代码行是no,no。

鉴于这一切,我想用一些会在Windows应用程序中更频繁地破坏nhibernate会话的内容替换上面的行。类似于HttpRequest的生命周期。我不想把它留给Windows客户端来了解nhibernate会话(或事务)以及何时打开和关闭它。我希望这是自动触发的。

问题

那么,我在哪里可以将nhibernate会话存储在一个允许我(即客户端以外的东西)的Windows应用程序中,根据每个数据库请求自动开始和结束事务(也就是说,每当客户端进行调用时到DB)?

** 更新 **

以下是另外两个关于如何实施工作单元格式的链接

http://msdn.microsoft.com/en-us/magazine/dd882510.aspx

http://www.codeinsanity.com/2008/09/unit-of-work-pattern.html

2 个答案:

答案 0 :(得分:1)

您的每个应用都可以提供IUnitOfWorkStorage

等界面的通用实现
public interface IUnitOfWorkStorage
{
    void StoreUnitOfWork(IUnitOfWork uow);
}

IUnitOfWork可以是ISession的包装器,它看起来像这样

public interface IUnitOfWork
{
   void Begin();
   void End();
}

Begin可能会打开会话并启动事务,而End将提交事务并关闭会话。因此,您可以拥有2个IUnitOfWorkStorage实现,一个用于WebApp,一个用于Windows App。 Web应用程序可以使用HttpContext.Current或其他东西,您的Windows应用程序只能提供一个简单的对象存储,它位于您的操作结束时将结束UnitOfWork。

答案 1 :(得分:1)

我最终使用以下代码。它给我的应用程序唯一的“负担”是单元测试,我宁愿用生成代码的会话特定信息来破解代码。我保留了与我的问题中提到的相同的代码,然后将此类添加到我的单元测试项目中:

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
    }
}

要使用上述课程,你可以这样做:

[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
}

注意:如果您正在使用此方法,请确保在执行来自Dao类的查询时不要将会话包装在“using”语句中,例如this post。因为你自己管理会话并且让它们比每个查询的单个会话更长时间打开,所以你需要摆脱所有关闭会话的地方并让NhibernateSessionModule为你做这个(网络应用程序或Windows应用程序) )。