asp.net-mvc – 具有Fluent nHibernate和Ninject的多租户.每个租
我正在构建一个多租户Web应用程序,出于安全考虑,我们需要为每个租户提供一个数据库实例.所以我有一个用于身份验证的MainDB和用于应用程序数据的许多ClientDB.
我在Ninject和Fluent nHibernate上使用Asp.net MVC.我已经在应用程序开始时在Ninject模块中使用Ninject和Fluent nHibernate设置了我的SessionFactory / Session / Repositories.我的会话是PerRequestScope,存储库也是. 我的问题是,现在我需要为每个租户实例化一个SessionFactory(SingletonScope)实例,只要其中一个连接到应用程序并为每个webrequest创建一个新会话和必要的存储库.我很困惑如何做到这一点,需要一个具体的例子. 这是情况. 应用程序启动:TenantX的用户输入他的登录信息.创建MainDB的SessionFactory并打开与MainDB的会话以对用户进行身份验证.然后应用程序创建auth cookie. 租户访问应用程序:租户名称ConnectionString从MainDB中提取,Ninject必须为该租户构建租户特定的SessionFactory(SingletonScope). Web请求的其余部分,所有需要存储库的控制器将根据该租户的SessionFactory注入租户特定的会话/存储库. 如何使用Ninject设置动态?当我有多个数据库时,我最初使用的是Named实例,但现在数据库是特定于租户的,我很遗憾…… 解决方法
经过进一步的研究,我可以给你一个更好的答案.
虽然可以将连接字符串传递给ISession.OpenSession,但更好的方法是创建自定义ConnectionProvider.最简单的方法是从DriverConnectionProvider派生并覆盖ConnectionString属性: public class TenantConnectionProvider : DriverConnectionProvider { protected override string ConnectionString { get { // load the tenant connection string return ""; } } public override void Configure(IDictionary<string,string> settings) { ConfigureDriver(settings); } } 使用FluentNHibernate,您可以像这样设置提供程序: var config = Fluently.Configure() .Database( MsSqlConfiguration.MsSql2008 .Provider<TenantConnectionProvider>() ) 每次打开会话时都会评估ConnectionProvider,从而允许您连接到应用程序中的特定于租户的数据库. 上述方法的一个问题是SessionFactory是共享的.如果您只使用第一级缓存(因为它与会话绑定),这不是一个真正的问题,但是如果您决定启用二级缓存(与SessionFactory绑定). 因此,建议的方法是每个租户使用一个SessionFactory(这将适用于每个租户的架构和每个租户的数据库策略). 经常被忽略的另一个问题是尽管二级缓存与SessionFactory相关联,但在某些情况下,缓存空间本身是共享的(reference).这可以通过设置提供程序的“regionName”属性来解决. 以下是基于您的要求的每个租户SessionFactory的工作实现. Tenant类包含为租户设置NHibernate所需的信息: public class Tenant : IEquatable<Tenant> { public string Name { get; set; } public string ConnectionString { get; set; } public bool Equals(Tenant other) { if (other == null) return false; return other.Name.Equals(Name) && other.ConnectionString.Equals(ConnectionString); } public override bool Equals(object obj) { return Equals(obj as Tenant); } public override int GetHashCode() { return string.Concat(Name,ConnectionString).GetHashCode(); } } 因为我们将存储Dictionary< Tenant,ISessionFactory>我们实现IEquatable接口,以便我们可以评估租户密钥. 获取当前租户的过程是这样抽象的: public interface ITenantAccessor { Tenant GetCurrentTenant(); } public class DefaultTenantAccessor : ITenantAccessor { public Tenant GetCurrentTenant() { // your implementation here return null; } } 最后是管理会话的NHibernateSessionSource: public interface ISessionSource { ISession CreateSession(); } public class NHibernateSessionSource : ISessionSource { private Dictionary<Tenant,ISessionFactory> sessionFactories = new Dictionary<Tenant,ISessionFactory>(); private static readonly object factorySyncRoot = new object(); private string defaultConnectionString = @"Server=(local)sqlexpress;Database=NHibernateMultiTenancy;integrated security=true;"; private readonly ISessionFactory defaultSessionFactory; private readonly ITenantAccessor tenantAccessor; public NHibernateSessionSource(ITenantAccessor tenantAccessor) { if (tenantAccessor == null) throw new ArgumentNullException("tenantAccessor"); this.tenantAccessor = tenantAccessor; lock (factorySyncRoot) { if (defaultSessionFactory != null) return; var configuration = AssembleConfiguration("default",defaultConnectionString); defaultSessionFactory = configuration.BuildSessionFactory(); } } private Configuration AssembleConfiguration(string name,string connectionString) { return Fluently.Configure() .Database( MsSqlConfiguration.MsSql2008.ConnectionString(connectionString) ) .Mappings(cfg => { cfg.FluentMappings.AddFromAssemblyOf<NHibernateSessionSource>(); }) .Cache(c => c.UseSecondLevelCache() .ProviderClass<HashtableCacheProvider>() .RegionPrefix(name) ) .ExposeConfiguration( c => c.SetProperty(NHibernate.Cfg.Environment.SessionFactoryName,name) ) .BuildConfiguration(); } private ISessionFactory GetSessionFactory(Tenant currentTenant) { ISessionFactory tenantSessionFactory; sessionFactories.TryGetValue(currentTenant,out tenantSessionFactory); if (tenantSessionFactory == null) { var configuration = AssembleConfiguration(currentTenant.Name,currentTenant.ConnectionString); tenantSessionFactory = configuration.BuildSessionFactory(); lock (factorySyncRoot) { sessionFactories.Add(currentTenant,tenantSessionFactory); } } return tenantSessionFactory; } public ISession CreateSession() { var tenant = tenantAccessor.GetCurrentTenant(); if (tenant == null) { return defaultSessionFactory.OpenSession(); } return GetSessionFactory(tenant).OpenSession(); } } 当我们创建NHibernateSessionSource的实例时,我们为我们的“默认”数据库设置了一个默认的SessionFactory. 当调用CreateSession()时,我们得到一个ISessionFactory实例.这将是默认会话工厂(如果当前租户为空)或租户特定会话工厂.定位租户特定会话工厂的任务由GetSessionFactory方法执行. 最后,我们在我们获得的ISessionFactory实例上调用OpenSession. 请注意,当我们创建会话工厂时,我们设置SessionFactory名称(用于调试/分析目的)和缓存区域前缀(出于上述原因). 我们的Io??C工具(在我的例子中是StructureMap)将所有内容连接起来: x.For<ISessionSource>().Singleton().Use<NHibernateSessionSource>(); x.For<ISession>().HttpContextScoped().Use(ctx => ctx.GetInstance<ISessionSource>().CreateSession()); x.For<ITenantAccessor>().Use<DefaultTenantAccessor>(); 这里NHibernateSessionSource的范围是每个请求的单例和ISession. 希望这可以帮助. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net-mvc-3 – Firefox页面没有正确重定向
- asp.net Web Api路由不工作
- asp.net-mvc – Razor如何创建一个CheckBox并使其为READONL
- asp.net-mvc – 导航菜单中突出显示在Asp.NET MVC?
- 获取ASP.NET会话上次访问时间(或超时)
- asp.net – 如何快速进行HTML渲染
- asp.net – VS插件:查看标记.存在这样的事情吗?
- asp.net-web-api – Web API帮助页面始终为空
- asp.net-mvc – 如果使用HTML内容,我可以写入内联吗?
- asp.net-mvc – 使用强类型对象保持控制器和视图分离