【问题标题】:StructureMap, NHibernate and multiple databasesStructureMap、NHibernate 和多个数据库
【发布时间】:2011-10-28 14:52:59
【问题描述】:

我正在使用 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>());
    }
}

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

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

【问题讨论】:

    标签: asp.net-mvc nhibernate inversion-of-control structuremap


    【解决方案1】:

    注册多个会话工厂很容易 - 问题在于在需要时选择正确的。例如,假设我们有某种具有多个数据库的实验室。每个实验室都有一个位置和该位置的多个样本。我们可以有一个 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。首先,您创建的是一个会话工厂工厂——这并不是我们想要的。我们想给我们的工厂一个位置并从中获得一个会话,而不是一个会话工厂。 Fluent 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。我知道那里有问题。

    【讨论】:

    • 我可能应该注意到,自从我使用 StructureMap 或 NHibernate 以来已经有好几年了——所以我可能在那里搞砸了。但基本模式应该是健全的。希望对您有所帮助!
    • 一个非常有用的答案。我目前正在使用 StrucutreMap 的 TheInstanceNamed() 将 SessionFactories 分配给 DAO,这似乎工作正常。当我有一点空闲时间时,会考虑实施你的建议。谢谢。
    【解决方案2】:

    你确定这东西有效吗? 字符串 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;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-09
      • 1970-01-01
      相关资源
      最近更新 更多