StructureMap, NHibernate и несколько баз данных - PullRequest
7 голосов
/ 28 октября 2011

Я работаю над приложением Asp.Net MVC 3, использующим Fluent NHibernate.Я просто пытаюсь добавить контейнер IoC, используя StructureMap.

Я реализовал фабрику пользовательских контроллеров, которая использует 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>());
    }
}

Все это прекрасно работает для одной базы данных и одной фабрики сеансов.Однако приложение использует несколько баз данных.

Как лучше всего справиться с этим?

Ответы [ 2 ]

9 голосов
/ 01 ноября 2011

Регистрация нескольких фабрик сессий проста - проблема в выборе правильного, когда вам это нужно. Например, допустим, у нас есть какая-то лаборатория с несколькими базами данных. Каждая лаборатория имеет местоположение и несколько образцов для этого места. Мы могли бы иметь 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>

Таким образом, нам нужна уникальная фабрика сеансов для каждой строки подключения. Давайте создадим NamedSessionFactory, который обертывает ISessionFactory:

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 голосов
/ 21 сентября 2013

Вы уверены, что это работает? string 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;
    }
}
...