7

我正在使用流利NHibernate的Asp.Net MVC 3應用程序。我只是試圖添加一個使用StructureMap的IoC容器。StructureMap,NHibernate和多個數據庫

我已經實現了一個自定義控制器工廠,它使用StructureMap來創建控制器並注入依賴關係。每個控制器構造函數接受一個或多個服務,然後將DAO作爲構造函數參數。每個DAO構造函數接受一個I​​SessionFactory。

對於我的StructureMap NHibernate的註冊表,我有以下:

internal class NHibernateRegistry : Registry 
{ 
    public NHibernateRegistry() 
    { 
     var connectionString = ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString; 

     For<ISessionFactory>() 
       .Singleton() 
       .Use(x => new AppSessionFactory().GetSessionFactory(connectionString)); 

     For<ISession>() 
      .HybridHttpOrThreadLocalScoped() 
      .Use(x => x.GetInstance<ISessionFactory>().OpenSession()); 
    } 

} 

public class AppSessionFactory 
{ 
    public ISessionFactory GetSessionFactory(string connectionString) 
    { 
     return GetConfig(connectionString) 
       .BuildSessionFactory(); 
    } 

    public static FluentConfiguration GetConfig(string connectionString) 
    { 
     return Fluently.Configure() 
      .Database(MsSqlConfiguration.MsSql2005.ConnectionString(x => x.Is(connectionString))) 
      .Mappings(
       x => x.FluentMappings.AddFromAssemblyOf<AppEntity>()); 
    } 
} 

這一切工作正常單個數據庫和單個會話工廠。但是應用程序使用多個數據庫。

處理這個問題的最佳方法是什麼?

回答

9

註冊多個會話工廠很容易 - 問題是在需要時選擇合適的工具。例如,假設我們有某種具有多個數據庫的實驗室。每個實驗室都有一個位置和該位置的多個樣本。我們可以有一個SampleRepository來模擬它。每個位置都有一個唯一的關鍵字來標識它(例如「LabX」,「LabY」,「BlackMesa」)。我們可以使用該唯一鍵作爲app.config文件中數據庫連接字符串的名稱。在這個例子中,我們將在app.config文件中有三個連接字符串。下面是一個示例connectionStrings節:

<connectionStrings> 
    <add name="LabX" connectionString="Data Source=labx;User ID=someuser;Password=somepassword"/> 
    <add name="LabY" connectionString="Data Source=laby;User ID=someuser;Password=somepassword"/> 
    <add name="BlackMesa" connectionString="Data Source=blackmesa;User ID=freemang;Password=crowbar"/> 
</connectionStrings> 

因此,我們需要爲每個連接字符串唯一的會話工廠。讓我們創建一個包裝ISessionFactory一個NamedSessionFactory:

public interface INamedSessionFactory 
{ 
    public string Name { get; } // The name from the config file (e.g. "BlackMesa") 
    public ISessionFactory SessionFactory { get; } 
} 

public class NamedSessionFactory : INamedSessionFactory 
{ 
    public string Name { get; private set; } 
    public ISessionFactory SessionFactory { get; private set; } 

    public NamedSessionFactory(string name, ISessionFactory sessionFactory) 
    { 
     Name = name; 
     SessionFactory = sessionFactory; 
    } 
} 

現在我們需要修改AppSessionFactory一點。首先,你創建的是一個會話工廠 - 這不是我們想要的。我們想給我們的工廠一個位置,並從中獲得一個會話,而不是會話工廠。流利的NHibernate是給我們會話工廠的。

public interface IAppSessionFactory 
{ 
    ISession GetSessionForLocation(string locationKey); 
} 

這裏的技巧是接受構造函數中的INamedSessionFactory對象列表。 StructureMap應該爲我們提供我們已註冊的所有INamedSessionFactory對象。我們將在一秒鐘內註冊。

public class AppSessionFactory : IAppSessionFactory 
{ 
    private readonly IList<INamedSessionFactory> _factories; 

    public AppSessionFactory(IEnumerable<INamedSessionFactory factories) 
    { 
     _factories = new List<INamedSessionFactory>(factories); 
    } 

這就是魔術發生的地方。給定一個位置關鍵字,我們遍歷我們的工廠列表,尋找與locationKey具有相同名稱的工廠列表,然後讓它打開一個會話並將其返回給調用者。

public ISession GetSessionForLocation(string locationKey) 
    { 
     var sessionFactory = _factories.Where(x => x.Name == locationKey).Single(); 

     return sessionFactory.OpenSession(); 
    } 
} 

現在讓我們把它們連接起來。

internal class NHibernateRegistry : Registry 
{ 
    public NHibernateRegistry() 
    { 

我們通過所有我們的app.config文件中的連接字符串去環(會有在這個例子中他們三人),並註冊爲每一個的INamedSessionFactory對象。

 foreach (ConnectionStringSettings location in ConfigurationManager.ConnectionStrings) 
     { 
      For<INamedSessionFactory>() 
       .Singleton() 
       .Use(x => new NamedSessionFactory(
        location.Name, 
        GetSessionFactory(location.ConnectionString)); 
     } 

我們還需要註冊IAppSessionFactory。

 For<IAppSessionFactory>() 
      .Singleton() 
      .Use<AppSessionFactory>(); 
    } 

您會注意到我們已經將該邏輯移出了工廠類...這些是從Fluent NHibernate創建會話工廠的輔助方法。

private static ISessionFactory GetSessionFactory(string connectionString) 
    { 
     return GetConfig(connectionString) 
       .BuildSessionFactory(); 
    } 

    public static FluentConfiguration GetConfig(string connectionString) 
    { 
     return Fluently.Configure() 
      .Database(MsSqlConfiguration.MsSql2005.ConnectionString(x => x.Is(connectionString))) 
      .Mappings(
       x => x.FluentMappings.AddFromAssemblyOf<AppEntity>()); 
    } 
} 

這應該做到這一點!讓我們創建一個存儲庫,在我們的樣品得到...

public class SampleRepository 
{ 
    private readonly IAppSessionFactory _factory; 

    public SampleRepository(IAppSessionFactory factory) 
    { 
     _factory = factory; 
    } 

    public IEnumerable<Sample> GetSamplesForLocation(Location location) 
    { 
     using (ISession session = _factory.GetSessionForLocation(location.Key) 
     { 
      foreach (Sample sample in session.Query<Sample>()) 
       yield return sample; 
     } 
    } 
} 

現在你可以SampleRepository的單個實例,並使用GetSamplesForLocation方法從任何我們在app.config中註冊了三個數據庫抽取樣品。可能要避免BlackMesa。我知道那裏有問題。

+0

我也許應該注意到,它已經多年,因爲我用StructureMap或NHibernate的 - 所以我可能會搞砸的東西在那裏。但基本模式應該是健全的。希望能幫助到你! –

+0

一個非常有用的答案。我目前使用StrucutreMap的TheInstanceNamed()來分配SessionFactor到DAO,這似乎工作正常。當我有一點空閒時間時,請考慮實施您的建議。謝謝。 – TonE

0

你確定這個東西有效嗎? 串ISessionFactory

public string ISessionFactory SessionFactory { get; private set; } 

這應該是

public interface INamedSessionFactory 
{ 
    ISessionFactory SessionFactory { get; set; } 
    string Name { get; } 
} 

public class NamedSessionFactory : INamedSessionFactory 
{ 
    public ISessionFactory SessionFactory { get; set; } 
    public string Name { get; private set; } 

    public NamedSessionFactory(string Name, ISessionFactory SessionFactory) 
    { 
     this.Name = Name; 
     this.SessionFactory = SessionFactory; 
    } 
} 
相關問題