さらなる研究の後、私はあなたに良い答えを与えることができます。
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が共有されていることです。第1レベルのキャッシュを使用している(これはセッションに結び付けられているため)ので、実際には問題にはなりませんが、第2レベルのキャッシュ(SessionFactoryに関連付けられている)を有効にすることにした場合は問題になりません。
したがって、テナントごとにSessionFactoryを設定することをお勧めします(これは、テナントごとのスキーマおよびテナントごとのデータベース戦略に適用されます)。
2番目のレベルのキャッシュはSessionFactoryに結びついていますが、キャッシュスペース自体が共有されている(reference)場合があります。これは、プロバイダの「regionName」プロパティを設定することで解決できます。
以下は、お客様の要件に基づいたテナントのSessionFactoryの実装です。我々はテナントキーを評価することができますので、我々はIEquatable
インターフェイスを実装Dictionary<Tenant, ISessionFactory>
を格納することがありますので
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();
}
}
:
Tenant
クラスは、私たちがテナントのためのNHibernateを設定するために必要な情報が含まれています。
現在のテナントを取得するプロセスはそれほどのように抽象化される:
セッションを管理
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
というインスタンスが生成されます。これはデフォルトのセッションファクトリ(現在のテナントがnullの場合)またはテナント固有のセッションファクトリのいずれかになります。テナント固有のセッションファクトリを見つける作業は、GetSessionFactory
メソッドによって実行されます。
最後に、取得したISessionFactory
インスタンスでOpenSession
と呼び出します。
セッションファクトリを作成するとき、セッションファクトリ名(デバッグ/プロファイリング目的)とキャッシュ領域接頭辞(上記の理由から)を設定することに注意してください。
当社のIoCツール(私の場合のStructureMap)のワイヤのすべてをバックアップ:ここ
x.For<ISessionSource>().Singleton().Use<NHibernateSessionSource>();
x.For<ISession>().HttpContextScoped().Use(ctx =>
ctx.GetInstance<ISessionSource>().CreateSession());
x.For<ITenantAccessor>().Use<DefaultTenantAccessor>();
をNHibernateSessionSourceは、要求ごとにシングルトンとISessionとしてスコープされています。
これが役に立ちます。
一度ユーザーがログインすれば、パスワードを変更できるようになりますか?私は彼らのパスワードがTenantDBではなく、 "MainDB"にあると仮定していますか? –