如何在MVC4项目中处理NHibernate会话

时间:2012-08-25 00:25:56

标签: nhibernate asp.net-mvc-4

我目前从globalasax获取会话如下......

public class MvcApplication : HttpApplication
{
    public static readonly ISessionFactory SessionFactory =                                        NHibernateHelper.CreateSessionFactory();

    public MvcApplication()
    {
        BeginRequest += delegate
                            {
                                if (!HttpContext.Current.Request.Url.AbsolutePath.StartsWith("/_cassette/"))
                                {
                                    CurrentSession = SessionFactory.OpenSession();
                                    CurrentSession.FlushMode = FlushMode.Auto;
                                }
                            };
        EndRequest += delegate
                        {
                            if (CurrentSession != null)
                            {
                                CurrentSession.Flush();
                                CurrentSession.Dispose();
                            }
                        };
    }

    public static ISession CurrentSession
    {
        get { return (ISession) HttpContext.Current.Items["current.session"]; }
        set { HttpContext.Current.Items["current.session"] = value; }

我正在查看Sharp Architecture Transaction属性和类似的http://weblogs.asp.net/srkirkland/archive/2009/09/03/asp-net-mvc-transaction-attribute-using-nhibernate.aspx,但是在MVC4项目中处理会话的最佳方式是使用非隐式事务ala http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions

我可以通过将事务/提交添加到begin request / end请求来轻松地包装所有内容,但是属性方法看起来更干净(实际上处理错误);或者我现在应该使用过滤器吗?

使用NHibernate的MVC4的最佳实践是什么?

4 个答案:

答案 0 :(得分:3)

您当前的会话处理存在一个严重问题(已经完成了;))。 CurrentSession是静态的,因此它在所有并发请求之间共享。 NHibernate的ISession不是线程安全的(不像ISessionFactory,它是线程安全的)。

NHibernate提供会话可以绑定到的会话上下文,之后可以从会话工厂(.GetCurrentSession() - method)获取绑定会话。为了能够像下一个示例中那样使用CurrentSessionContext,您需要告诉NHibernate使用哪个会话上下文。对于Web应用程序,WebSessionContext是不错的选择。

当我使用MVC时,我编写了一个处理会话处理的动作过滤器。这是一个例子(为MVC 2编写):

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class TransactionAttribute : ActionFilterAttribute
{
    public TransactionAttribute()
    {
        Order = 100;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        CurrentSessionContext.Bind(NHibernateManager.SessionFactory.OpenSession());
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var session = CurrentSessionContext.Unbind(NHibernateManager.SessionFactory);
        session.Close();
        session.Dispose();
    }
}

将事务管理添加到同一过滤器中也不应该是一个太大的问题。在OnActionExecuting-method中,您可以使用ISession的.BeginTransaction()打开事务,在OnActionExecuted中,您可以从ISession的Transaction-property获取当前事务,然后可以提交和处理该事务。

答案 1 :(得分:2)

另一种实现"每个请求模式的会话模式" - httpModule。

public class NHibernateModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.BeginRequest += context_BeginRequest;
        context.EndRequest += context_EndRequest;
    }

    private static void context_BeginRequest(object sender, EventArgs e)
    {
        //use my session manager
        ISession session = SessionManager.Instance.OpenSession();
        CurrentSessionContext.Bind(session);
    }

    private static void context_EndRequest(object sender, EventArgs e)
    {
        ISessionFactory sessionFactory = SessionManager.Instance.SessionFactory;
        ISession session = CurrentSessionContext.Unbind(sessionFactory);

        if (session == null) return;
        if (session.Transaction != null)
        {
            if (session.Transaction.IsActive)
            {
                //if there is an active session, commit it
                session.Transaction.Commit();
            }
            else
            {
                //
                session.Transaction.Rollback();
            }
        }

        session.Close();
    }


<configuration>

   <!-- IIS 6 -->
    <system.web>
         <httpModules>
            <add name="NHibernateModule" type="NHibernateModule"/>
        </httpModules>
     </system.web>
     <!-- IIS 7 and Cassini. -->
    <system.webServer>
         <modules>
            <add name="NHibernateModule" type="NHibernateModule"/>
        </modules>
    </system.webServer>
</configuration>

ActionFilterAttribute方式有一个问题:在一个HTTP请求中如何处理一些操作?

此模式表明每个HTTP请求都会打开一个NHibernate会话。

答案 2 :(得分:0)

跟随Ultor的回答,加上Ayende的“重构无摩擦和无气味的代码:交易怎么样?”文章,最后,因为我已经使用以下内容在Application_Start中设置了应用程序的依赖项解析器:

DependencyResolver.SetResolver(new MyDependencyResolver())

我将TransactionAttribute类更改为如下:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class SessionAttribute : ActionFilterAttribute {
    static readonly ISessionFactory SessionFactory = BuildSessionFactory();

    static ISessionFactory BuildSessionFactory() {
        return (ISessionFactory) DependencyResolver.Current.GetService(typeof (ISessionFactory));
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext) {
        var sessionController = filterContext.Controller as SessionController;

        if (sessionController == null)
            return;
        if (sessionController.NHibernateSession == null) {
            sessionController.NHibernateSession = SessionFactory.OpenSession();
        }
        sessionController.NHibernateSession.BeginTransaction();
        CurrentSessionContext.Bind(sessionController.NHibernateSession);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext) {
        var sessionController = filterContext.Controller as SessionController;

        if (sessionController == null) return;

        var session = CurrentSessionContext.Unbind(SessionFactory);
        if (session == null) return;
        if (session.Transaction != null) {
            if (!session.Transaction.IsActive) return;

            if (filterContext.Exception != null)
                session.Transaction.Rollback();
            else
                session.Transaction.Commit();
        }
        session.Close();
        session.Dispose();
    }
}

基本控制器定义如下:

public class SessionController : Controller {
    public ISession NHibernateSession { get; set; }
}

现在,我的控制器中的持久性变得如此简单:

[HttpGet, Session]
public ActionResult CreateOrUpdate(Guid id = new Guid()) {
    var company = GetCompany(id);
    if (company == null) throw new HttpException(404, "Not Found");
    return View(company);
}

[HttpPost, ValidateAntiForgeryToken, Session] 
public ActionResult CreateOrUpdate(Company passedInCompany) {
    var company = NHibernateSession.Get<Company>(passedInCompany.Id);
    if (company == null) throw new HttpException(404, "Not Found");
    UpdateModel(company);
    if (ModelState.IsValid) {
        NHibernateSession.SaveOrUpdate(company);
        return RedirectToAction("Index");
    }
    return View(company);
}

Company GetCompany(Guid id) {
    Company company;
    if (id == Guid.Empty) {
        company = companyBuilder.Create();
    } else {
        company = NHibernateSession.Get<Company>(id);
        NHibernateSession.Flush();
    }
    return company;
}

答案 3 :(得分:0)

这里有一些很好的答案,但我的建议是使用依赖注入框架(我喜欢Ninject)来实现每个请求的会话。这允许您在控制器上使用构造函数注入来注入ISession。