【问题标题】:Entity Framework and Migration with multiple schemas in a single Context在单个上下文中具有多个模式的实体框架和迁移
【发布时间】:2013-03-06 22:53:18
【问题描述】:

我正在使用 Entity Framework Code First 开发一个多租户应用程序。每个租户在数据库中都有不同的模式,但应用程序将为所有租户提供一个上下文和模型。

Entity Framwork 6 能够在同一个环境中使用具有多个上下文的多个模式 数据库,但我没有找到一种方法可以在单个上下文中使用多个模式。

我已生成迁移(通过命令行)到默认的“dbo”架构。我想使用这些迁移更新其他架构。

【问题讨论】:

  • 只是好奇,为什么必须使用单一上下文?为什么不使用多个上下文?
  • 该产品将作为 SaaS 销售。所有客户都将拥有相同的型号。我需要一种无需更改代码即可创建和维护客户架构的方法
  • @BrunoAlbanodeSouza:据我所知,每个上下文都有一个模式,但您可以让上下文接受构造函数中的模式,然后创建一个 contextfactory 方法,该方法根据参数连接到正确的模式。除此之外,这不会是一种千篇一律的情况。 DbContext 与一个特定的模式结合。
  • @BradChristie 我无法找到将多模式与迁移一起使用的方法......我将尝试不同的方法。谢谢!

标签: c# entity-framework ef-code-first entity-framework-6


【解决方案1】:

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

在每个模型的映射配置中,您可以调用“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 多个相同的表时,您的 dba 可能会尖叫,而不是在每个表上使用 customerID 列.

【讨论】:

    【解决方案2】:

    查看这些帖子

    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);
    
        }
    }
    

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

    希望对你有帮助

    【讨论】:

      【解决方案3】:

      最好将解决方案视为 Robert Petz 和 alfkonne 答案与其他一些围绕连接管理和迁移管理的工具的组合。对于客户特定的备份目的,我更喜欢每个客户的数据库级别而不是模式。 您可以正确执行特定于模式的备份/恢复 IF 设置。但请确保所涉及的任何外部工具都处理基于模式的还原。

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          猜你喜欢
          • 2016-09-15
          • 1970-01-01
          • 1970-01-01
          • 2017-03-09
          • 2015-12-05
          • 1970-01-01
          • 2013-01-31
          • 2016-03-16
          • 1970-01-01
          相关资源
          最近更新 更多