2011-05-06 105 views
3

雖然公司標準是使用NHibernate(因爲這是所有項目的標準),我正在編寫一個新的應用程序,而我正在使用ASP.NET MVC因爲它現在已經成熟了。我實現了我的交易中的控制器(據稱是你應該做的方式),所以它看起來像這樣在我的根控制器:NHibernate會話+事務使用ASP.NET MVC 3

[TransactionPerRequest] 
public class FbsController : Controller 
{ 

} 

然後我所有的控制器從這個FbsController繼承。這樣做的原因是因爲我的所有動作中有90%會轉移到數據庫,所以創建一個事務併爲其餘10%的動作(很少執行)處置它的開銷並不值得用[TransactionPerRequest]裝飾每個動作。

總是難倒我的事情是關於NHibernate會話。在沿東西我都行庫類這一點,雖然這是在其他項目不同:

public void Add(User user) 
    { 
     using (ISession session = NHibernateHelper.OpenSession()) 
     { 
      session.Save(user); 
     } 
    } 

    public void Remove(User user) 
    { 
     using (ISession session = NHibernateHelper.OpenSession()) 
     { 
      session.Delete(user); 
     } 
    } 

    public User GetById(int userId) 
    { 
     using (ISession session = NHibernateHelper.OpenSession()) 
     { 
      return session.QueryOver<User>() 
       .Where(c => c.UserID == userId) 
       .SingleOrDefault(); 
     } 
    } 

所以後來在我的庫中的大部分功能,我不得不打開會話。有什麼辦法可以避免這種行爲,所以我不必在每個存儲庫方法中打開一個會話?這似乎有點反直覺,因爲我通常必須爲每一個人做。我想知道其他人的解決方案對於我看到的以多種方式圍繞代碼散亂的交易&問題。

現實我希望我的存儲庫的方法如下所示:

public void Add(User user) 
    { 
     session.Save(user); 
    } 

    public void Remove(User user) 
    { 
     session.Delete(user); 
    } 

    public User GetById(int userId) 
    { 
     return session.QueryOver<User>() 
      .Where(c => c.UserID == userId) 
      .SingleOrDefault(); 
    } 

隨着一切正在處理含蓄。

回答

10
+1

這沒問題,但如果您使用存儲庫模式並需要訪問存儲庫中的會話,該怎麼辦?他沒有提到,該會話僅在控制器中可用,因此使用QueryOver向數據庫發出的請求需要從控制器完成。 – Kezzer 2011-05-06 09:04:36

+0

@Kezzer,你可以在'HttpContext.Current.Items [「NHibernateSession」]周圍編寫一個包裝類,然後配置你的DI框架把它傳遞給你的倉庫構造函數。 – 2011-05-06 09:08:21

+0

非常好的鏈接,這正是我正在尋找的。謝謝! – Tx3 2011-11-13 11:06:04

0

我用StructureMap自動啓動一個會話時的Isession首次調用,然後通過HttpRequest的緩存會話。這讓我可以在整個請求中使用延遲加載和事務處理,並且最少的編碼煩惱。

下面是我的引導程序的代碼,它使用Fluent NHibernate和StructureMap爲我設置了一切。

public class Bootstrapper 
{ 
    public static ISessionFactory DBSessionFactory { get; set; } 
    public static ISession DBSession { get; set; } 

    public static void InitializeObjectFactory() 
    { 
     ObjectFactory.Initialize(x => 
            { 
             x.PullConfigurationFromAppConfig = true; 
             x.Scan(y => 
                { 
                 y.Assembly(Assembly.GetAssembly(typeof(AccountController))); 
                 y.Assembly(Assembly.GetAssembly(typeof(IMyProject))); 
                 y.WithDefaultConventions(); 
                } 
              ); 

             // these are for NHibernate 
             x.ForRequestedType<ISessionFactory>() 
              .CacheBy(InstanceScope.Singleton) 
              .TheDefault.Is.ConstructedBy(GetDBSessionFactory); 

             // open session at beginning of every http request 
             // (the session is disposed at end of http request in global.asax's Application_EndRequest) 
             x.ForRequestedType<ISession>() 
              .CacheBy(InstanceScope.HttpContext) 
              .TheDefault.Is.ConstructedBy(GetDBSession); 
            }); 
    } 

    public static ISessionFactory CreateSessionFactory() 
    { 
     return GetFluentConfiguration() 
      .BuildSessionFactory(); 
    } 

    public static ISessionFactory GetDBSessionFactory() 
    { 
     if (DBSessionFactory == null) 
     { 
      DBSessionFactory = CreateSessionFactory(); 
     } 
     return DBSessionFactory; 
    } 

    public static ISession GetDBSession() 
    { 
     if (DBSession == null) 
     { 
      DBSession = CreateSession(); 
     } 
     return DBSession; 
    } 

    public static ISession CreateSession() 
    { 
     return GetDBSessionFactory() 
      .OpenSession(); 
    } 

    public static FluentConfiguration GetFluentConfiguration() 
    { 
     string commandTimeout = ConfigurationManager.AppSettings["MyDBCommandTimeout"]; 
     return Fluently.Configure() 
      .Database(// use your db configuration) 
      .Mappings(m => 
          { 
           m.HbmMappings 
            .AddFromAssemblyOf<MyEO>(); 
           m.FluentMappings 
            .AddFromAssemblyOf<MyEO>() 
            .AddFromAssemblyOf<MyEOMap>(); 
          }) 
      .ExposeConfiguration(
       cfg => 
        { 
         // command_timeout sets the timeout for the queries 
         cfg.SetProperty("command_timeout", commandTimeout); 
        } 
      ); 
    } 
} 

呼叫Bootstrapper.InitializeObjectFactory();在全局的Application_Start()方法中。ASAX,像這樣:

protected void Application_Start() 
{ 
    RegisterRoutes(RouteTable.Routes); 
    Bootstrapper.InitializeObjectFactory(); 
    ... 
} 

在Application_EndRequest()關閉會話:

protected void Application_EndRequest() 
{ 
    // ensure that we aren’t leaking ISessions on every web request 
    if (Bootstrapper.DBSession != null) 
    { 
     if (Bootstrapper.DBSession.IsOpen) 
     { 
      Bootstrapper.DBSession.Close(); 
     } 
     Bootstrapper.DBSession.Dispose(); 
     Bootstrapper.DBSession = null; 
    } 

    HttpContextBuildPolicy.DisposeAndClearAll(); 
} 

現在,你只需要調用

ObjectFactory.GetInstance<ISession>() 
從任何地方

(我把它包在一個輔助類保持我的代碼簡單),並且StructureMap將爲您提供緩存會話。

4

我做線沿線的東西:

在我的Global.asax.cs:

public static ISessionFactory SessionFactory { get; set; } 

,然後在Application_Start定義:

protected void Application_Start() 
{ 
    AreaRegistration.RegisterAllAreas(); 
    RegisterRoutes(RouteTable.Routes); 

    var nhConfig = new Configuration().Configure(); 
    SessionFactory = nhConfig.BuildSessionFactory(); 
} 

然後創建這個類:

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

    protected ISessionFactory sessionFactory 
    { 
     get 
     { 
       return MvcApplication.SessionFactory; 
     } 
    } 

    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     var session = sessionFactory.OpenSession(); 
     CurrentSessionContext.Bind(session); 
     session.BeginTransaction(); 
    } 

    public override void OnResultExecuted(ResultExecutedContext filterContext) 
    { 
     var session = CurrentSessionContext.Unbind(sessionFactory); 
     if (session != null) 
     { 
      if (session.Transaction.IsActive) 
      { 
       try 
       { 
        session.Transaction.Commit(); 
       } 
       catch 
       { 
        session.Transaction.Rollback(); 
       } 
      } 
      session.Close(); 
     } 
    } 
} 

然後我的通用倉庫看起來沿着線的東西:

public class Repository<T> : IRepository<T> 
{ 
    private readonly ISessionFactory SessionFactory; 
    public Repository(ISessionFactory sessionFactory) 
    { 
     SessionFactory = sessionFactory; 
    } 
    public ISession Session 
    { 
     get 
     { 
      return SessionFactory.GetCurrentSession(); 
     } 
    } 
    public T Get(long id) 
    { 
     return Session.Get<T>(id); 
    } 
} 

我的一個倉庫的具體實現是:

public class CmsContentRepository : Repository<CmsContent>, ICmsContentRepository 
{ 
    public CmsContentRepository(ISessionFactory sessionFactory) : base(sessionFactory) { } 
} 

還有一件事我再裝點像這樣我的控制器:

[NHSession] 
public ViewResult Revisions(int id) 
{ 
    var model = Service.CmsContentRepository.Get(id); 
    return View("Revisions", model); 
} 

這使我可以在一個請求中使用一個工作單元。基本上,一個請求進入並開始一個會話,SessionFactory被傳入存儲庫(ies)的構造函數。我在這裏使用DI,但這是可選的。如果檢測到錯誤,那麼會話將被回滾,如果不是在請求結束時提交的話。我會推薦NHProf,因爲它可以幫助您理解會話管理(即,如果設置不正確)。