3

我目前从 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 项目中处理会话以利用非隐式事务的最佳方式http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions

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

MVC4 与 NHibernate 的最佳实践是什么?

4

4 回答 4

3

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

NHibernate 提供会话上下文,会话可以绑定到其中,之后可以从会话工厂获取绑定的会话(.GetCurrentSession() -方法)。为了能够像在下一个示例中那样使用 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 获取当前事务,然后可以提交和处置。

于 2012-08-25T05:30:27.337 回答
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 会话。

于 2012-08-25T21:43:51.600 回答
0

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

于 2013-10-01T20:05:46.797 回答
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;
}
于 2013-09-26T20:50:51.773 回答