База данных для каждого клиента с плавным NHibernate и StructureMap

#database #asp.net-mvc-3 #fluent-nhibernate #structuremap #multi-tenant

#База данных #asp.net-mvc-3 #свободно-nhibernate #structuremap #многопользовательский

Вопрос:

В настоящее время я использую StructureMap для внедрения NHibernateRegistry экземпляра в мой DAL, который настраивает NHibernate для одной строки подключения и загружает Singleton FluentConfiguration для моего однопользовательского приложения.

Как я должен изменить свою конфигурацию Fluent NHibernate, чтобы использовать другую базу данных на основе {tenant} параметра маршрутизации в моем URL маршрутизации?

Пример маршрутизации:

 {tenant}/{controller}/{action}/{id}
  

…где запросы для branch1/Home/Index и branch2/Home/Index используют один и тот же код приложения, но разные базы данных для извлечения отображаемых данных.

В прошлом я решал эту проблему для StructureMap и LINQ, вводя TenantContext объект для каждого запроса, который извлекал параметр маршрутизации из HttpContext принятого им в качестве параметра конструктора и указывал другой контекст данных LINQ.

Однако я подозреваю, что NHibernate справляется с этим лучше, чем я мог бы придумать.

Частичный NHibernateRegistry класс

 public class NHibernateRegistry : Registry
{
    // ... private vars here

    public NHibernateRegistry()
    {
        var cfg = Fluently.Configure()
            .Database(MsSqlConfiguration
                .MsSql2008.ConnectionString(c => 
                    c.FromConnectionStringWithKey("TenantConnectionStringKey")))
                    // where to inject this key?
            .ExposeConfiguration(BuildSchema)
            .Mappings(x => 
                x.FluentMappings.AddFromAssembly(typeof(UserMap).Assembly)

        For<FluentConfiguration>().Singleton().Use(cfg);

        var sessionFactory = cfg.BuildSessionFactory();

        For<ISessionFactory>().Singleton()
            .Use(sessionFactory);
        For<ISession>().HybridHttpOrThreadLocalScoped()
            .Use(x => x.GetInstance<ISessionFactory>().OpenSession());
        For<IUnitOfWork>().HybridHttpOrThreadLocalScoped()
            .Use<UnitOfWork>();
        For<IDatabaseBuilder>().Use<DatabaseBuilder>();

    }
}
  

конфигурация StructureMap:

 public static class Bootstrapper
{
    public static void ConfigureStructureMap()
    {
        ObjectFactory.Initialize(Init);
    }

    private static void Init(IInitializationExpression x)
    {
        x.AddRegistry(new NHibernateRegistry()); // from Data project
    }
}
  

Я новичок в NHibernate, поэтому я не уверен в области охвата своих сеансов и конфигураций. Есть ли у NHibernate встроенный способ справиться с этим?

Ответ №1:

У меня это сработало в модуле a

 return Fluently.Configure()
            .Database(MsSqlConfiguration.MsSql2008.ConnectionString(x => x.FromConnectionStringWithKey("IMB"))
            .Cache(c => c.UseQueryCache().QueryCacheFactory<StandardQueryCacheFactory>()
                         .RegionPrefix("IMB")
                         .ProviderClass<HashtableCacheProvider>()
                         .UseMinimalPuts()).UseReflectionOptimizer())
            .Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.Load("IMB.Data")))
           .Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.Load("IMB.Security")))

           .ExposeConfiguration(
            c => c.SetProperty("current_session_context_class", "web"))
            .ExposeConfiguration(cfg => _configuration = cfg)
            .BuildSessionFactory();
  

Комментарии:

1. Спасибо, я попробую. Являются ли все .RegionPrefix("IMB") (предположительно, идентификатор клиента) родными для NHibernate?

2. Нет .RegionPrefix — это префикс кэша, который fn использует для обработки кэшированных объектов

3. при ближайшем рассмотрении это не влияет на строку подключения, основанную на каких-либо идентификаторах клиентов. Насколько это многопользовательское решение?

4. Извините, я неправильно понял ваш вопрос. Вы можете попробовать это. ссылка , откуда вы вызываете свой загрузчик? глобальный.asax?

Ответ №2:

Проблема в том, что вы действительно хотите, чтобы ваш ISessionFactory объект был одноэлементным. Это означает, что лучше не указывать строку подключения при создании ISessionFactory . Вы пробовали создавать ISessionFactory без указания строки подключения, а затем передавать созданное вручную соединение в ISessionFactory.OpenSession ?

Например:

 public ISession CreateSession()
{
    string tennantId = GetTennantId();
    string connStr = ConnectionStringFromTennant(tennantId);
    SqlConnection conn = new SqlConnection(connStr);
    conn.Open();
    session = sessionFactory.OpenSession(conn);
}
  

А затем скажите StructureMap вызвать этот метод.

Недостатком является то, что вы не можете сейчас создавать схему базы данных при создании ISessionFactory , но, возможно, создание схем баз данных в веб-приложениях в любом случае не такая уж отличная идея?