在单个上下文中使用多个模式的实体框架和迁移

本文关键字:模式 实体 框架 迁移 上下文 单个 | 更新日期: 2023-09-27 18:04:42

我正在开发一个多租户应用程序,首先使用实体框架代码。每个租户在数据库中都有不同的模式,但是应用程序对所有租户都有一个Context和模型。

Entity framework 6能够在同一个框架中使用具有多个上下文的多个模式数据库,但是我没有找到在单个上下文中使用多个模式的方法。

我已经生成了迁移(通过命令行)到默认的" dbo "模式。我想使用这些迁移来更新其他模式。

在单个上下文中使用多个模式的实体框架和迁移

虽然我同意多上下文绝对是更好的方法(这就是我自己的项目设置),但我想回答您关于如何在单个上下文中使用多个模式的原始问题:

在每个模型的映射配置中,你可以调用'ToTable(myTableName, mySchema)'来修改表所属的模式:

public class MyEntityMap : EntityTypeConfiguration<MyEntity>
{
    public MyEntityMap ()
    {
        HasKey(t => t.MyId);
        Property(t => t.MyId)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        ToTable("MyEntity", "MySchema");
    }
}

这将允许您分别为每个表设置模式,同时保持单个上下文。

既然您声明希望在不同的模式中使用相同的模型,那么在不了解您的设置的情况下,这使得它变得有点困难。如果您只处理少数客户,并且不介意在代码中维护他们的模式,那么您可以简单地为每个模式创建一个映射(如上所述),然后为每个客户添加一个新的DbSet。如果您试图将其扩展到大量客户,那么我强烈建议您使用不同的方法,因为您的dba可能会尖叫,当他看到100多个相同的表在不同的模式下,而不是在每个表上使用一个customerID列。

看这些帖子

http://thedatafarm.com/data-access/digging-in-to-multi-tenant-migrations-with-ef6-alpha/

http://romiller.com/2011/05/23/ef-4-1-multi-tenant-with-code-first/

我想出了这个上下文

public class DataLayerBuilder : DbContext
{
    private static  string conStr = string.Empty ;
    private DataLayerBuilder(DbConnection connection, DbCompiledModel model)
    : base(connection, model, contextOwnsConnection: false){ }
    public DbSet<Person> People { get; set; }
    private static ConcurrentDictionary<Tuple<string, string>, DbCompiledModel> modelCache
        = new ConcurrentDictionary<Tuple<string, string>, DbCompiledModel>();
    /// <summary>
    /// Creates a context that will access the specified tenant
    /// </summary>
    public static DataLayerBuilder Create(string tenantSchema)
    {
        conStr = ConfigurationManager.ConnectionStrings["ConnSTRName"].ConnectionString;
        var connection = new SqlConnection(conStr);
        var compiledModel = modelCache.GetOrAdd(
            Tuple.Create(conStr, tenantSchema),
            t =>
            {
                var builder = new DbModelBuilder();
                builder.HasDefaultSchema(tenantSchema);
                builder.Entity<Person>().ToTable("People");                   
                builder.Entity<Contact>().ToTable("Contacts");
                var model = builder.Build(connection);
                return model.Compile();
            });
        return new DataLayerBuilder(connection, compiledModel);
    }
    /// <summary>
    /// Creates the database and/or tables for a new tenant
    /// </summary>
    public static void ProvisionTenant(string tenantSchema)
    {
        try
        {
            using (var ctx = Create(tenantSchema))
            {
                if (!ctx.Database.Exists())
                {
                    ctx.Database.Create();
                }
                else
                {
                    ctx.Database.Initialize(true);
                }
            }
        }
        catch (Exception)
        {
            throw;
        }
    }
}
到目前为止,我已经能够使用以下代码添加多个租户
 public void ProvisionTest()
    {
        //Arrange
        var tenant = "test2";
        //Act
        DataLayerBuilder.ProvisionTenant(tenant);
    }
}

改进上面的代码,我认为你可以写一个简单的函数来更新每个用户的表结构

希望对大家有所帮助

最好的解决方案是将Robert Petz和alfkonne的答案结合起来,再加上一些围绕连接管理和迁移管理的工具。对于客户特定的备份目的,我更喜欢DB级别而不是每个客户的模式。您可以正确地执行模式特定的备份/恢复IF设置。但是要确保所涉及的任何外部工具都能处理基于模式的恢复。

可以在一个上下文中使用多个方案。在每个实体或类中,必须添加以下数据注释:

[Table("TableName", Shema = "ShemaName")]
public class Entity
{
}