Kev*_*ffe 26 c# entity-framework
我们的组织需要拥有一个数据库,多租户
(通过表模式,而不是租户ID)架构.
这里有一篇很棒的文章来开始讨论这类事情:http: //romiller.com/2011/05/23/ef-4-1-multi-tenant-with-code-first/
在文章的中间,写道:
您会注意到(可能有点沮丧)我们需要编写代码来为每个实体配置表模式.不可否认,围绕此代码的魔术独角兽并不多......在EF的未来版本中,我们将能够用更清洁的自定义约定来替换它.
我们的目标是拥有一个最简单的方法,可以使用一个上下文类来连接到具有相同模型的多个模式.
(请注意,modelBuilder.HasDefaultSchema似乎不够,因为它仅适用于EF首次初始化上下文并运行OnModelCreating时)
EF5或EF6中是否存在上述清洁自定义约定?
或者是否有更清洁的方式来处理这个?
注意:我也在开发论坛上问了这个问题,因为它似乎更多地与EF的方向有关,但是想看看这里是否有人有替代方案.
注2:我不担心迁移,我们会分开处理.
Tob*_* J. 37
如果您在您的房屋modelBuilder.HasDefaultSchema上实施,该房产OnModelCreating就足够了.模型创建一次,然后由内部缓存,您可以为缓存定义自己的密钥.将模式名称作为模型缓存键,EF将根据每个不同的缓存键(在我们的示例中为模式)创建模型.这是我的概念证明代码:IDbModelCacheKeyProviderDbContextEntityFramwork
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TenantDataModel;
namespace TenantDataContext
{
public class TenantDataCtx : DbContext, IDbModelCacheKeyProvider
{
#region Construction
public static TenantDataCtx Create(string databaseServer, string databaseName, string databaseUserName, string databasePassword, Guid tenantId)
{
var connectionStringBuilder = new System.Data.SqlClient.SqlConnectionStringBuilder();
connectionStringBuilder.DataSource = databaseServer;
connectionStringBuilder.InitialCatalog = databaseName;
connectionStringBuilder.UserID = databaseUserName;
connectionStringBuilder.Password = databasePassword;
string connectionString = connectionStringBuilder.ToString();
return new TenantDataCtx(connectionString, tenantId);
}
// Used by EF migrations
public TenantDataCtx()
{
Database.SetInitializer<TenantDataCtx>(null);
}
internal TenantDataCtx(string connectionString, Guid tenantId)
: base(connectionString)
{
Database.SetInitializer<TenantDataCtx>(null);
this.SchemaName = tenantId.ToString("D");
}
public string SchemaName { get; private set; }
#endregion
#region DataSet Properties
public DbSet<TestEntity> TestEntities { get { return this.Set<TestEntity>(); } }
#endregion
#region Overrides
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
if (this.SchemaName != null)
{
modelBuilder.HasDefaultSchema(this.SchemaName);
}
base.OnModelCreating(modelBuilder);
}
#endregion
#region IDbModelCacheKeyProvider Members
public string CacheKey
{
get { return this.SchemaName; }
}
#endregion
}
}
Run Code Online (Sandbox Code Playgroud)
此外,我找到了一种使用EF迁移的方法.我对我的解决方案并不满意,但似乎现在没有其他解决方案可用.
using System;
using System.Collections.Generic;
using System.Data.Entity.SqlServer;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TenantDatabaseManager
{
public class SqlServerSchemaAwareMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
private string _schema;
public SqlServerSchemaAwareMigrationSqlGenerator(string schema)
{
_schema = schema;
}
protected override void Generate(System.Data.Entity.Migrations.Model.AddColumnOperation addColumnOperation)
{
string newTableName = _GetNameWithReplacedSchema(addColumnOperation.Table);
var newAddColumnOperation = new System.Data.Entity.Migrations.Model.AddColumnOperation(newTableName, addColumnOperation.Column, addColumnOperation.AnonymousArguments);
base.Generate(newAddColumnOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.AddPrimaryKeyOperation addPrimaryKeyOperation)
{
addPrimaryKeyOperation.Table = _GetNameWithReplacedSchema(addPrimaryKeyOperation.Table);
base.Generate(addPrimaryKeyOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.AlterColumnOperation alterColumnOperation)
{
string tableName = _GetNameWithReplacedSchema(alterColumnOperation.Table);
var newAlterColumnOperation = new System.Data.Entity.Migrations.Model.AlterColumnOperation(tableName, alterColumnOperation.Column, alterColumnOperation.IsDestructiveChange);
base.Generate(newAlterColumnOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.DropPrimaryKeyOperation dropPrimaryKeyOperation)
{
dropPrimaryKeyOperation.Table = _GetNameWithReplacedSchema(dropPrimaryKeyOperation.Table);
base.Generate(dropPrimaryKeyOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.CreateIndexOperation createIndexOperation)
{
string name = _GetNameWithReplacedSchema(createIndexOperation.Table);
createIndexOperation.Table = name;
base.Generate(createIndexOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.CreateTableOperation createTableOperation)
{
string newTableName = _GetNameWithReplacedSchema(createTableOperation.Name);
var newCreateTableOperation = new System.Data.Entity.Migrations.Model.CreateTableOperation(newTableName, createTableOperation.AnonymousArguments);
newCreateTableOperation.PrimaryKey = createTableOperation.PrimaryKey;
foreach (var column in createTableOperation.Columns)
{
newCreateTableOperation.Columns.Add(column);
}
base.Generate(newCreateTableOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.RenameTableOperation renameTableOperation)
{
string oldName = _GetNameWithReplacedSchema(renameTableOperation.Name);
string newName = renameTableOperation.NewName.Split(new char[] { '.' }).Last();
var newRenameTableOperation = new System.Data.Entity.Migrations.Model.RenameTableOperation(oldName, newName, renameTableOperation.AnonymousArguments);
base.Generate(newRenameTableOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.RenameIndexOperation renameIndexOperation)
{
string tableName = _GetNameWithReplacedSchema(renameIndexOperation.Table);
var newRenameIndexOperation = new System.Data.Entity.Migrations.Model.RenameIndexOperation(tableName, renameIndexOperation.Name, renameIndexOperation.NewName);
base.Generate(newRenameIndexOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.AddForeignKeyOperation addForeignKeyOperation)
{
addForeignKeyOperation.DependentTable = _GetNameWithReplacedSchema(addForeignKeyOperation.DependentTable);
addForeignKeyOperation.PrincipalTable = _GetNameWithReplacedSchema(addForeignKeyOperation.PrincipalTable);
base.Generate(addForeignKeyOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.DropColumnOperation dropColumnOperation)
{
string newTableName = _GetNameWithReplacedSchema(dropColumnOperation.Table);
var newDropColumnOperation = new System.Data.Entity.Migrations.Model.DropColumnOperation(newTableName, dropColumnOperation.Name, dropColumnOperation.AnonymousArguments);
base.Generate(newDropColumnOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.RenameColumnOperation renameColumnOperation)
{
string newTableName = _GetNameWithReplacedSchema(renameColumnOperation.Table);
var newRenameColumnOperation = new System.Data.Entity.Migrations.Model.RenameColumnOperation(newTableName, renameColumnOperation.Name, renameColumnOperation.NewName);
base.Generate(newRenameColumnOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.DropTableOperation dropTableOperation)
{
string newTableName = _GetNameWithReplacedSchema(dropTableOperation.Name);
var newDropTableOperation = new System.Data.Entity.Migrations.Model.DropTableOperation(newTableName, dropTableOperation.AnonymousArguments);
base.Generate(newDropTableOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.DropForeignKeyOperation dropForeignKeyOperation)
{
dropForeignKeyOperation.PrincipalTable = _GetNameWithReplacedSchema(dropForeignKeyOperation.PrincipalTable);
dropForeignKeyOperation.DependentTable = _GetNameWithReplacedSchema(dropForeignKeyOperation.DependentTable);
base.Generate(dropForeignKeyOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.DropIndexOperation dropIndexOperation)
{
dropIndexOperation.Table = _GetNameWithReplacedSchema(dropIndexOperation.Table);
base.Generate(dropIndexOperation);
}
private string _GetNameWithReplacedSchema(string name)
{
string[] nameParts = name.Split('.');
string newName;
switch (nameParts.Length)
{
case 1:
newName = string.Format("{0}.{1}", _schema, nameParts[0]);
break;
case 2:
newName = string.Format("{0}.{1}", _schema, nameParts[1]);
break;
case 3:
newName = string.Format("{0}.{1}.{2}", _schema, nameParts[1], nameParts[2]);
break;
default:
throw new NotSupportedException();
}
return newName;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这就是我使用的方式SqlServerSchemaAwareMigrationSqlGenerator:
// Update TenantDataCtx
var tenantDataMigrationsConfiguration = new DbMigrationsConfiguration<TenantDataContext.TenantDataCtx>();
tenantDataMigrationsConfiguration.AutomaticMigrationsEnabled = false;
tenantDataMigrationsConfiguration.SetSqlGenerator("System.Data.SqlClient", new SqlServerSchemaAwareMigrationSqlGenerator(schemaName));
tenantDataMigrationsConfiguration.SetHistoryContextFactory("System.Data.SqlClient", (existingConnection, defaultSchema) => new HistoryContext(existingConnection, schemaName));
tenantDataMigrationsConfiguration.TargetDatabase = new System.Data.Entity.Infrastructure.DbConnectionInfo(connectionString, "System.Data.SqlClient");
tenantDataMigrationsConfiguration.MigrationsAssembly = typeof(TenantDataContext.TenantDataCtx).Assembly;
tenantDataMigrationsConfiguration.MigrationsNamespace = "TenantDataContext.Migrations.TenantData";
DbMigrator tenantDataCtxMigrator = new DbMigrator(tenantDataMigrationsConfiguration);
tenantDataCtxMigrator.Update();
Run Code Online (Sandbox Code Playgroud)
来自德国,
托比亚斯