私が必要とした答えのほとんどはここで見つかりました:
http://romiller.com/2011/05/23/ef-4-1-multi-tenant-with-code-first/
public partial class MyDBContext
{
public MyDBContext() : base() { }
private MyDBContext(DbConnection connection, DbCompiledModel model) : base(connection, model, contextOwnsConnection: false) { }
private static ConcurrentDictionary<Tuple<string, string>, DbCompiledModel> modelCache
= new ConcurrentDictionary<Tuple<string, string>, DbCompiledModel>();
public static MyDBContext Create(string tenantSchema, DbConnection connection)
{
var compiledModel = modelCache.GetOrAdd
(
Tuple.Create(connection.ConnectionString, tenantSchema),
t =>
{
var builder = new DbModelBuilder();
builder.Conventions.Remove<IncludeMetadataConvention>();
builder.Entity<Location>().ToTable("Locations", tenantSchema);
builder.Entity<User>().ToTable("Users", tenantSchema);
var model = builder.Build(connection);
return model.Compile();
}
);
var context = new FmsDBContext(connection, compiledModel);
if( !string.IsNullOrEmpty( tenantSchema ) && !tenantSchema.Equals( "dbo", StringComparison.OrdinalIgnoreCase ) )
{
var objectContext = ( (IObjectContextAdapter)context ).ObjectContext;
objectContext.Connection.Open();
var currentUser = objectContext.ExecuteStoreQuery<UserContext>( "SELECT CURRENT_USER AS Name", null ).FirstOrDefault();
if( currentUser.Name.Equals( tenantSchema, StringComparison.OrdinalIgnoreCase ) )
{
var executeAs = string.Format( "REVERT; EXECUTE AS User = '{0}';", tenantSchema );
objectContext.ExecuteStoreCommand( executeAs );
}
}
return context;
}
}
次に、次のようにスキーマの情報にアクセスできます。
using (var db = MyDBContext.Create( schemaName, dbConn ))
{
// ...
}
ただし、これは実際にデータベースユーザーを使用することをバイパスします。スキーマ名を指定するだけでなく、データベースユーザーのコンテキストを使用する方法に取り組んでいます。
アップデート:
私はついにここで最後のハードルを解決しました。キーは次のコードでした:
if( !string.IsNullOrEmpty( tenantSchema ) && !tenantSchema.Equals( "dbo", StringComparison.OrdinalIgnoreCase ) )
{
var objectContext = ( (IObjectContextAdapter)context ).ObjectContext;
objectContext.Connection.Open();
var currentUser = objectContext.ExecuteStoreQuery<UserContext>( "SELECT CURRENT_USER AS Name", null ).FirstOrDefault();
if( currentUser.Name.Equals( tenantSchema, StringComparison.OrdinalIgnoreCase ) )
{
var executeAs = string.Format( "REVERT; EXECUTE AS User = '{0}';", tenantSchema );
objectContext.ExecuteStoreCommand( executeAs );
}
}
EXECUTE ASコマンドは、後でlinqtoentityコマンドを実行するために使用される前に接続で発行されます。接続が開いたままである限り、ユーザーのコンテキストはそのまま残ります。私のデータベースでは、テナントのスキーマ名とユーザー名は同じです。
ユーザーの実行コンテキストを複数回変更するとエラーが発生するため、クイッククエリを使用して現在のユーザーコンテキストを判別します。接続を使用して情報を取得するには、小さなエンティティクラスが必要です。
private class UserContext
{
public string Name { get; set; }
}