StructureMap,NHibernate和多个数据库

Ton*_*onE 7 structuremap nhibernate asp.net-mvc inversion-of-control

我正在使用Fluent NHibernate开发Asp.Net MVC 3应用程序.我只是尝试使用StructureMap添加IoC容器.

我已经实现了一个自定义控制器工厂,它使用StructureMap来创建控制器并注入依赖项.每个控制器构造函数都接受一个或多个服务,而这些服务又将DAO作为构造函数参数.每个DAO构造函数都使用一个ISessionFactory.

对于我的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>());
    }
}
Run Code Online (Sandbox Code Playgroud)

这一切都适用于单个数据库和单个会话工厂.但是应用程序使用多个数据库.

处理这个问题的最佳方法是什么?

And*_*y S 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>
Run Code Online (Sandbox Code Playgroud)

因此,我们需要为每个连接字符串建立一个唯一的会话工厂.让我们创建一个包装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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们需要稍微修改你的AppSessionFactory.首先,你创建的是一个会议工厂工厂 - 这不是我们想要的.我们希望为我们的工厂提供一个位置并从中获取会话,而不是会话工厂.流畅的NHibernate为我们提供了会话工厂.

public interface IAppSessionFactory
{
    ISession GetSessionForLocation(string locationKey);
}
Run Code Online (Sandbox Code Playgroud)

这里的技巧是接受构造函数中的INamedSessionFactory对象列表.StructureMap应该为我们提供已注册的所有INamedSessionFactory对象.我们将在一秒钟内完成注册.

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

    public AppSessionFactory(IEnumerable<INamedSessionFactory factories)
    {
        _factories = new List<INamedSessionFactory>(factories);
    }
Run Code Online (Sandbox Code Playgroud)

这就是魔术发生的地方.给定一个位置键,我们运行我们的工厂列表,寻找一个与locationKey同名的工厂,然后让它打开一个会话并将其返回给调用者.

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

        return sessionFactory.OpenSession();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在让我们把它们连在一起.

internal class NHibernateRegistry : Registry
{
    public NHibernateRegistry()
    {
Run Code Online (Sandbox Code Playgroud)

我们将遍历app.config文件中的所有连接字符串(在此示例中将有三个连接字符串)并为每个连接字符串注册一个INamedSessionFactory对象.

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

我们还需要注册IAppSessionFactory.

        For<IAppSessionFactory>()
          .Singleton()
          .Use<AppSessionFactory>();
    }
Run Code Online (Sandbox Code Playgroud)

您会注意到我们已将此逻辑移出工厂类......这些是从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>());
    }
}
Run Code Online (Sandbox Code Playgroud)

应该这样做!让我们创建一个存储库来获取我们的样本......

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以获取SampleRepository的单个实例,并使用GetSamplesForLocation方法从我们在app.config中注册的三个数据库中的任何一个中提取样本.可能想要避免使用BlackMesa.我知道那里有问题.