Управление несколькими базами данных с помощью NHibernate и Autofac - PullRequest
8 голосов
/ 14 декабря 2010

Я думал, что у меня возникнет этот вопрос, пока я сам нашел решение.

После создания основной части приложения у меня есть требование в последнюю минуту поддержать чтение / запись в дополнительную базу данных (всего 2, других не известно). Я построил приложение, используя NHibernate, и Autofac поставлял компоненты DI / IoC. Кстати, это находится в приложении ASP.NET MVC 2.

У меня есть общий класс репозитория, который принимает сеанс NHibernate. Теоретически я могу продолжать использовать этот общий репозиторий (IRepository<>) для второй базы данных, если сеанс, который ему передается, порождается из соответствующего SessionFactory, верно?

Хорошо, когда приложение запускается, 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 я, вероятно, помещал бы экземпляры отдельных SessionFactories в хеш и извлекал их по мере необходимости. Я не хочу переделывать всю эту архитектуру, так как мы довольно близки к основной версии. Итак, я предполагаю, что мне нужно изменить хотя бы описанные выше методы, чтобы я мог независимо настроить две SessionFactories. Моя серая область - как я буду указывать правильную фабрику, которая будет использоваться с конкретным репозиторием (или, по крайней мере, для сущностей, специфичных для этой второй базы данных).

Кто-нибудь имеет опыт работы с этим сценарием при использовании контейнера IoC и NHibernate таким образом?

EDIT Я заглушил метод GetSessionFactory, который принимает путь к файлу конфигурации, проверяет наличие соответствующего SessionFactory в HttpRuntime.Cache, создает новый экземпляр, если он еще не существует, и возвращает этот SessionFactory. Теперь мне все еще нужно выяснить, как сообщить Autofac, как и когда указывать соответствующий путь конфигурации. Новый метод выглядит так (заимствовано из поста Билли за 2006 год здесь ):

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 Ответ

11 голосов
/ 16 декабря 2010

Я предполагаю, что вы хотите, чтобы разные типы сущностей входили в каждую базу данных;если вы хотите сохранить одинаковые типы сущностей в каждой базе данных, проверьте AutofacContrib.Multitenant.

В этом сценарии могут помочь два компонента:

Во-первых, используйте namedуслуги для ссылки на две разные базы данных.Я назову их "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])));

(Предупреждение - скомпилировано и протестировано в Google Chrome;))

Теперь, разрешение IRepository<MyEntity> выберет соответствующий сеанс, и сеансы будут по-прежнему лениво инициализироваться и корректно утилизироваться Autofac.

Вам, конечно, придется тщательно подумать об управлении транзакциями.

Надеюсь, это поможет.! NB

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...