2012-03-06 70 views
1

我很新的DI和IoC模式。如何使用注入存儲庫的Isessionfactory管理與ninject和Nhibernate的事務?

public class LazySessionContext 
{ 
    private readonly ISessionFactoryImplementor factory; 
    private const string CurrentSessionContextKey = "NHibernateCurrentSession"; 

    public LazySessionContext(ISessionFactoryImplementor factory) 
    { 
     this.factory = factory; 
    } 

    /// <summary> 
    /// Retrieve the current session for the session factory. 
    /// </summary> 
    /// <returns></returns> 
    public ISession CurrentSession() 
    { 
     Lazy<ISession> initializer; 
     var currentSessionFactoryMap = GetCurrentFactoryMap(); 
     if (currentSessionFactoryMap == null || 
      !currentSessionFactoryMap.TryGetValue(factory, out initializer)) 
     { 
      return null; 
     } 
     return initializer.Value; 
    } 

    /// <summary> 
    /// Bind a new sessionInitializer to the context of the sessionFactory. 
    /// </summary> 
    /// <param name="sessionInitializer"></param> 
    /// <param name="sessionFactory"></param> 
    public static void Bind(Lazy<ISession> sessionInitializer, ISessionFactory sessionFactory) 
    { 
     var map = GetCurrentFactoryMap(); 
     map[sessionFactory] = sessionInitializer; 
    } 

    /// <summary> 
    /// Unbind the current session of the session factory. 
    /// </summary> 
    /// <param name="sessionFactory"></param> 
    /// <returns></returns> 
    public static ISession UnBind(ISessionFactory sessionFactory) 
    { 
     var map = GetCurrentFactoryMap(); 
     var sessionInitializer = map[sessionFactory]; 
     map[sessionFactory] = null; 
     if (sessionInitializer == null || !sessionInitializer.IsValueCreated) return null; 
     return sessionInitializer.Value; 
    } 

    /// <summary> 
    /// Provides the CurrentMap of SessionFactories. 
    /// If there is no map create/store and return a new one. 
    /// </summary> 
    /// <returns></returns> 
    private static IDictionary<ISessionFactory, Lazy<ISession>> GetCurrentFactoryMap() 
    { 
     var currentFactoryMap = (IDictionary<ISessionFactory, Lazy<ISession>>) 
           HttpContext.Current.Items[CurrentSessionContextKey]; 
     if (currentFactoryMap == null) 
     { 
      currentFactoryMap = new Dictionary<ISessionFactory, Lazy<ISession>>(); 
      HttpContext.Current.Items[CurrentSessionContextKey] = currentFactoryMap; 
     } 
     return currentFactoryMap; 
    } 
} 

public interface ISessionFactoryProvider 
{ 
    IEnumerable<ISessionFactory> GetSessionFactories(); 
} 

public class SessionFactoryProvider 
{ 
    public const string Key = "NHibernateSessionFactoryProvider"; 
} 

public class NHibernateSessionModule : IHttpModule 
{ 
    private HttpApplication app; 

    public void Init(HttpApplication context) 
    { 
     app = context; 
     context.BeginRequest += ContextBeginRequest; 
     context.EndRequest += ContextEndRequest; 
     context.Error += ContextError; 
    } 

    private void ContextBeginRequest(object sender, EventArgs e) 
    { 
     var sfp = (ISessionFactoryProvider)app.Context.Application[SessionFactoryProvider.Key]; 
     foreach (var sf in sfp.GetSessionFactories()) 
     { 
      var localFactory = sf; 
      LazySessionContext.Bind(
       new Lazy<ISession>(() => BeginSession(localFactory)), 
       sf); 
     } 
    } 

    private static ISession BeginSession(ISessionFactory sf) 
    { 
     var session = sf.OpenSession(); 
     session.BeginTransaction(); 
     return session; 
    } 

    private void ContextEndRequest(object sender, EventArgs e) 
    { 
     var sfp = (ISessionFactoryProvider)app.Context.Application[SessionFactoryProvider.Key]; 
     var sessionsToEnd = sfp.GetSessionFactories() 
           .Select(LazySessionContext.UnBind) 
           .Where(session => session != null); 

     foreach (var session in sessionsToEnd) 
     { 
      EndSession(session); 
     } 
    } 

    private void ContextError(object sender, EventArgs e) 
    { 
     var sfp = (ISessionFactoryProvider)app.Context.Application[SessionFactoryProvider.Key]; 
     var sessionstoAbort = sfp.GetSessionFactories() 
           .Select(LazySessionContext.UnBind) 
           .Where(session => session != null); 

     foreach (var session in sessionstoAbort) 
     { 
      EndSession(session, true); 
     } 
    } 

    private static void EndSession(ISession session, bool abort = false) 
    { 
     if (session.Transaction != null && session.Transaction.IsActive) 
     { 
      if (abort) 
      { 
       session.Transaction.Rollback(); 
      } 
      else 
      { 
       session.Transaction.Commit(); 
      } 
     } 
     session.Dispose(); 
    } 

    public void Dispose() 
    { 
     app.BeginRequest -= ContextBeginRequest; 
     app.EndRequest -= ContextEndRequest; 
     app.Error -= ContextError; 
    } 
} 

我通過chinooknugets該樣品並從GitHub jfmarillo。從上面的代碼我將會話注入存儲庫並通過IHttpmodule控制事務。現在有兩個問題對我來說:

  1. 如果我通過代碼實現事務管理上面,將被稱爲每當有一個請求,它會打開一個會話該請求。這是實施「每次請求會話」方法的唯一目的。但是,如果在我的所有控制器方法中,我只有一種方法實際使用存儲庫,那麼我不想在每次發出請求時都打開會話。只有在控制器中由Transaction屬性標記的動作纔會處理事務。

  2. 我會提出一個請求,會話只會在存儲庫請求時打開,所以可以通過任何IoC容器來實現。

  3. 我仍然希望httpcontext事件來處理事務,以便context + = BegingRequest和context + = EndRequest。並且可以根據請求使用httpcontext在httpmodule內部處理事務。但我不想實現IhttpModule並將其放入web.config。這種方法還有其他的選擇嗎?

  4. 會話打開和關閉只會在httpcontext中完成,但是我想通過IoC容器(最好是ninject)來管理它,但只有當存儲庫正在請求該會話時。請注意,調用控制器時可能會初始化存儲庫,但不應打開該存儲庫中的會話。當實際存儲庫執行任何瞬態動作時,會話應該打開。

有人會澄清我應該遵循什麼實踐這種情況下?我用ninject和nhibernate使用Mvc 3。

回答

5

1,2) Ninject允許使用Ninject.Extensions.Factory 3.0.0

class MyController 
{ 
    public MyController(Lazy<SomeRepository> repository) { ... } 
} 

這種方式使用時,創建的存儲庫(和上下文)懶惰對象創建。

3) 爲什麼要爲此使用HttpModule?有更容易的方式例如爲:

kernel.Bind<ISession>() 
     .ToMethod(ctx => ctx.Kernel.Get<ISessionFactory().OpenSession()) 
     .InRequestScope() 
     .OnActivation(session => OpenTransaction(session)) 
     .OnDeactivation(session => EndTransaction(session)); 

從Ninject開始3.0.0可以在web.config中添加了一個的HttpModules結合,而不是將它們註冊和使用construcotr注入他們。但是由於如果使用上下文,HttpModule不知道你必須打開所有請求的事務。

+0

我同意,但在這種情況下不是這樣,如果我有一個控制器中的10個動作,然後http模塊會打開和關閉每個請求的連接,因爲它在請求範圍內?是不是因爲我可能只需要打開會話以及控制器中只有一個動作的事務,而不是在每個請求中處理和打開(這在HttpModule情況下實際發生的情況下),都不會處理和打開? – Joy 2012-03-07 10:19:58

+0

我會嘗試解決方案。 nd同時告知你可以告訴Lazy 語句是什麼?懶惰究竟在這裏做什麼? – Joy 2012-03-07 10:23:43

+0

我已經標記了你的答案,但唯一的問題是我的問題仍然沒有解決的地方,如果我不需要會話,而是打開和關閉綁定onactivation會話仍然是一個矯枉過正:) – Joy 2012-03-07 12:16:36

相關問題