2010-12-14 55 views
8

多個數據庫,我想我會得到這個問題在那裏,而我noodled對我自己的解決方案。管理與NHibernate和Autofac

具有內置了大量的應用程序後,我有一個最後一分鐘的要求,以支持讀取/寫入到一個附加的數據庫(2總,沒有已知的其他)。我使用NHibernate構建應用程序,Autofac提供DI/IoC組件。 FWIW,它位於ASP.NET MVC 2應用程序中。

我有一個通用的存儲庫類,帶有一個NHibernate的會議。理論上,只要傳遞給它的會話是從適當的SessionFactory產生的,我可以繼續爲第二個數據庫使用此通用存儲庫(IRepository<>),對吧?

好了,應用程序啓動時,Autofac做它的事。至於Session和SessionFactory的,我有一個規定模塊:

builder.Register(c => c.Resolve<ISessionFactory>().OpenSession()) 
    .InstancePerMatchingLifetimeScope(WebLifetime.Request) 
    .OnActivated(e => 
    { 
     e.Context.Resolve<TransactionManager>().CurrentTransaction = ((ISession)e.Instance).BeginTransaction(); 
    }); 

builder.Register(c => ConfigureNHibernate()) 
    .SingleInstance(); 

其中ConfigureNHibernate(),它返回基地SessionFactory的,看起來像:

private ISessionFactory ConfigureNHibernate() 
{ 
    Configuration cfg = new Configuration().Configure(); 
    cfg.AddAssembly(typeof(Entity).Assembly); 
    return cfg.Configure().BuildSessionFactory(); 
} 

目前,這僅限於剛剛一個數據庫。在任何其他NHib場景中,我可能會將單獨的SessionFactory的實例推送到散列中,並根據需要檢索它們。我不想重新設計整個事物,因爲我們離主要版本相當近。所以,我猜我至少需要修改上面的方法,以便我可以獨立配置兩個SessionFactory。我的灰色地帶是我將如何指定正確的Factory與特定存儲庫一起使用(或至少對於特定於第二個數據庫的實體)。

任何人都在使用這種方式的IoC容器和NHibernate有這種情況的經驗嗎?

編輯 我掐滅,需要一個配置文件路徑的方法的getSessionFactory,在HttpRuntime.Cache匹配的SessionFactory是否存在等檢查,創建一個新的實例,如果一個已經不存在,和返回SessionFactory。現在我仍然需要了解如何告訴Autofac如何以及何時指定合適的配置路徑。新方法看起來像(比利2006年後here大量借款):

private ISessionFactory GetSessionFactory(string sessionFactoryConfigPath) 
    { 
     Configuration cfg = null; 
     var sessionFactory = (ISessionFactory)HttpRuntime.Cache.Get(sessionFactoryConfigPath); 

     if (sessionFactory == null) 
     { 
      if (!File.Exists(sessionFactoryConfigPath)) 
       throw new FileNotFoundException("The nhibernate configuration file at '" + sessionFactoryConfigPath + "' could not be found."); 

      cfg = new Configuration().Configure(sessionFactoryConfigPath); 
      sessionFactory = cfg.BuildSessionFactory(); 

      if (sessionFactory == null) 
      { 
       throw new Exception("cfg.BuildSessionFactory() returned null."); 
      } 

      HttpRuntime.Cache.Add(sessionFactoryConfigPath, sessionFactory, null, DateTime.Now.AddDays(7), TimeSpan.Zero, System.Web.Caching.CacheItemPriority.High, null); 
     } 

     return sessionFactory; 
    } 
+1

將會話工廠存儲在緩存中是一個壞主意。這不是可以消失並被重新創造的東西。 – 2010-12-14 22:51:48

+0

我知道。實際上,我將不得不管理該緩存對象的生命週期。比利的例子實際上更進一步,創建了一個單例範圍的SessionManager類來處理緩存問題。我試圖貶低它,所以我最終可以讓Autofac通過它自己的(非常優雅的)範圍機制來管理它。 – nkirkes 2010-12-14 23:11:09

回答

11

我假設你想在不同種類的實體進入每個數據庫的;如果您想在每個數據庫中保留相同類型的實體,請查看AutofacContrib.Multitenant。

兩種成分可與這種情況下幫助是:在這一個

首先,使用命名服務,指的是兩個不同的數據庫。我會稱它們爲"db1""db2「。所有涉及到數據庫,一路攀升到會話的組成部分,獲得註冊一個名字:

builder.Register(c => ConfigureDb1()) 
    .Named<ISessionFactory>("db1") 
    .SingleInstance(); 

builder.Register(c => c.ResolveNamed<ISessionFactory>("db1").OpenSession()) 
    .Named<ISession>("db1") 
    .InstancePerLifetimeScope(); 

// Same for "db2" and so-on. 

現在,假設你有一個類型NHibernateRepository<T>接受一個ISession作爲其構造函數的參數,和你可以編寫函數WhichDatabase(Type entityType),當給定實體的類型時,返回"db1""db2"

我們使用ResolvedParameter根據實體類型動態選擇會話。

builder.RegisterGeneric(typeof(NHibernateRepository<>)) 
    .As(typeof(IRepository<>)) 
    .WithParameter(new ResolvedParameter(
     (pi, c) => pi.ParameterType == typeof(ISession), 
     (pi, c) => c.ResolveNamed<ISession>(
      WhichDatabase(pi.Member.DeclaringType.GetGenericArguments()[0]))); 

(警告 - 編譯和谷歌瀏覽器測試;))

現在,解決IRepository<MyEntity>將選擇適當的會話和會話將繼續懶洋洋地初始化和Autofac正確配置。當然,你將不得不仔細考慮交易管理。

希望這個竅門! NB

+1

夥計,就是這樣。好棒!我爲我的場景做了一些調整,並且更深入地挖掘了更新的Autofac源代碼,以便了解發生了什麼,但是,是的,這就是我喜歡的原因!謝謝尼古拉斯! – nkirkes 2010-12-15 23:47:39

+0

非常感謝 - 不客氣! – 2010-12-16 01:43:33