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

私はSharpArchitectureTransaction属性と同様の属性を見ていましたhttp://weblogs.asp.net/srkirkland/archive/2009/09/03/asp-net-mvc-transaction-attribute-using-nhibernate.aspxしかしwhats http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactionsで暗黙的でないトランザクションを利用するためのMVC4プロジェクトでセッションを処理する最良の方法

トランザクション/コミットを開始リクエスト/終了リクエストに追加することですべてを簡単にラップできますが、属性メソッドはよりクリーンに見えます(実際にはエラーを処理します)。または、今フィルターを使用する必要がありますか?

NHibernateを使用したMVC4のベストプラクティスは何ですか?

4

4 に答える 4

3

現在のセッション処理には1つの重大な問題があります(それが行われている場合;))。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から現在のトランザクションを取得し、コミットして破棄することができます。

于 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の方法には、1つのHTTPリクエストでいくつかのアクションを実行するとどのように動作するかという疑問があります。

このパターンは、HTTPリクエストごとに1つのNHibernateセッションが開かれることを示しています。

于 2012-08-25T21:43:51.600 に答える
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 に答える
0

ここにはいくつかの良い答えがありますが、依存性注入フレームワーク(私はNinjectが好きです)を使用してリクエストごとのセッションを実装することをお勧めします。これにより、コントローラーでコンストラクターインジェクションを使用してISessionをインジェクトでき​​ます。

于 2013-10-01T20:05:46.797 に答える