C# 在EF中IDatabaseInitializer的正确用法是什么?

C# 在EF中IDatabaseInitializer的正确用法是什么?,c#,entity-framework,ef-code-first,entity-framework-migrations,entity-framework-6,C#,Entity Framework,Ef Code First,Entity Framework Migrations,Entity Framework 6,我有一个自定义数据库初始化器,如下所示 /// <summary> /// Implements the IDatabaseInitializer to provide a custom database initialisation for the context. /// </summary> /// <typeparam name="TContext">TContext is the DbContext</typeparam> public

我有一个自定义数据库初始化器,如下所示

/// <summary>
/// Implements the IDatabaseInitializer to provide a custom database initialisation for the context.
/// </summary>
/// <typeparam name="TContext">TContext is the DbContext</typeparam>
public class ParikshaDataBaseInitializer<TContext> : IDatabaseInitializer<TContext> where TContext : DbContext
{
    /// <summary>
    /// The method to Initialise the database.
    /// Takes care of the database cannot be dropped since it is in use problem while dropping and recreating the database.
    /// </summary>
    /// <param name="context">The DbContext on which to run the initialiser</param>
    public void InitializeDatabase(TContext context)
    {
        var exists = context.Database.Exists();

        try
        {
            if (exists && context.Database.CompatibleWithModel(true))
            {
                // everything is good , we are done
                return;
            }

            if (!exists)
            {
                context.Database.Create();
            }
        }
        catch (Exception)
        {
            //Something is wrong , either we could not locate the metadata or the model is not compatible.
            if (exists)
            {
                context.Database.ExecuteSqlCommand("ALTER DATABASE Pariksha SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
                context.Database.ExecuteSqlCommand("USE Master DROP DATABASE Pariksha");
                context.SaveChanges();
            }

            context.Database.Create();
        }
    } 
}
一些论坛在构造函数中也提出了这一点,这似乎有点奇怪,因为我不想每次使用上下文时都检查db和模式是否正确。那么,这项技术的可能用途是什么?或者我是否认为这项建议的上下文是错误的

public ParikshaContext() : base("Pariksha")
        {           
          Database.SetInitializer(new ParikshaDataBaseInitializer<ParikshaContext>());
        }
public ParikshaContext():base(“Pariksha”)
{           
SetInitializer(新的ParikshaDataBaseInitializer());
}
总之,

  • 对于可用的不同技术,正确的用例是什么

  • 当我们将数据库从一个环境移动到另一个环境时,理想的策略是什么,以便迁移能够在所有条件下工作


  • 这是我尝试的
    Db初始值设定项
    ,它将
    迁移
    初始值设定项和默认的
    Db Create
    初始值设定项与种子设定相结合。(注意:这并不理想,更像是一个简单的练习,但它为您在这里提出的问题提供了一个解决方案,大部分是有效的-只需检查我所做的所有更新)

    至于
    为什么
    如何
    ——为了充分理解这一点,我建议您也参考(这是新版本,但在许多方面类似)

    1) a)Db初始值设定项通常只被调用一次(每个连接),并且当您第一次尝试访问“模型”时(第一次查询或类似查询)。在初始值设定项中放置断点以进行检查

    所以,将它放在构造函数中是完全安全的(尽管我更喜欢在启动时使用它,配置也一样)只有在需要初始化时才会调用它(并且使用了
    最后一组
    ),您不应该手动调用它

    无论如何,要强制初始化,您可以执行
    this.Database.Initialize(force:true)

    有关切换连接的详细信息,请参阅我关于问题的帖子

    b)如果您创建了自己的
    IDatabaseInitializer
    并且仍然希望迁移并行运行

    您不应该只从外部调用
    DbMigrator
    ,因为您的自定义初始值设定项将错过整个“db创建”(例如,如果您希望种子或其他内容,请查看上面的示例)

    这两个东西都是有效的“初始值设定者”——因此您需要将它们集成到一个,这将
    以某种方式链接
    东西。请记住,
    执行顺序
    非常重要(有关问题,请参见上面的示例)-您应该检查“空条件”,然后调用
    DbMigrator
    ,然后进行自己的初始化。我使用一个初始值设定项作为基类,并合并了另一个

    如果您只想
    种子
    ,您可以使用迁移配置,这是最简单的(如果可能的话)

    2) 是相当“开放式”的,没有单一的答案。通常它是有效的,但问题会扩大

    • 迁移是三件事(在我看来)-您的代码模型/实体、数据库/表和数据库中的
      \uu MigrationHistory
      系统表。所有三个都需要保持同步。如果出现“不同步”,则可以删除迁移表,重新创建迁移(带有保留现有数据库的标志),然后像以前一样继续—即,有一些解决方案可用于实时数据。为此,请看

    • 移动数据库时,您需要有删除/创建数据库的权限

    • 确保您的连接正确(配置中的更改-并与DbContext名称或ctor同步)

    • 保持简单,不要做花哨的事情或从代码切换连接(可能有问题),等等

    • 不要混用数据库/代码
    版本-即一个代码实体版本-一个数据库。如果您希望使用不同的代码版本(例如,登台、生产)共享同一个Db,则不要(多租户解决方案将在EF6中提供,例如)

  • 如果需要手动应用数据库-通过
    更新数据库生成
    脚本
    -并应用该脚本,请不要手动操作,否则会出错(迁移历史记录表)-请参阅

  • …仅举几个例子。在我看来,它是相当稳定和可用的——但是如果你遵循规则——并且知道限制是什么


    类CreateAndMigrateDatabaseInitializer
    :CreateDatabaseIfNotExists,IDatabaseInitializer
    其中TContext:DbContext
    其中TConfiguration:DbMigrationsConfiguration,new()
    {
    专用只读DbMigrationsConfiguration\u配置;
    public CreateAndMigrateDatabaseInitializer()
    {
    _配置=新的TConfiguration();
    }
    public CreateAndMigrateDatabaseInitializer(字符串连接)
    {
    Contract.Requires(!string.IsNullOrEmpty(connection),“connection”);
    _配置=新的t配置
    {
    TargetDatabase=新的DbConnectionInfo(连接)
    };
    }
    void IDatabaseInitializer.InitializeDatabase(TContext上下文)
    {
    var doseed=!context.Database.Exists();
    //&&new DatabaseTableChecker()。AnyModelTableExists(上下文);
    //检查是否要播种-我们“缺少”了“AnyModelTableExists”
    //…如果需要,可以复制/执行其他操作。。。
    var migrator=新的DbMigrator(_配置);
    //if(doseed | |!context.Database.CompatibleWithModel(false))
    if(migrator.GetPendingMigrations().Any())
    migrator.Update();
    //继续使用“种子”的“CreateDatabaseIfNotExists”
    base.InitializeDatabase(上下文);
    if(doseed)
    {
    种子(上下文);
    SaveChanges();
    }
    }
    受保护的覆盖无效种子(TContext上下文)
    {
    }
    }
    
    总之,

    1) what is the correct use-case for the different techniques available ?
    
    2) what would be the ideal strategy so that the migrations work in all conditions 
    and when we move databases from one environment to another ?
    
    退后一步,问问你自己,你什么时候想让英孚来做这件事
    public ParikshaContext() : base("Pariksha")
            {           
              Database.SetInitializer(new ParikshaDataBaseInitializer<ParikshaContext>());
            }
    
    class CreateAndMigrateDatabaseInitializer<TContext, TConfiguration> 
        : CreateDatabaseIfNotExists<TContext>, IDatabaseInitializer<TContext>
        where TContext : DbContext
        where TConfiguration : DbMigrationsConfiguration<TContext>, new()
    {
        private readonly DbMigrationsConfiguration _configuration;
        public CreateAndMigrateDatabaseInitializer()
        {
            _configuration = new TConfiguration();
        }
        public CreateAndMigrateDatabaseInitializer(string connection)
        {
            Contract.Requires(!string.IsNullOrEmpty(connection), "connection");
    
            _configuration = new TConfiguration
            {
                TargetDatabase = new DbConnectionInfo(connection)
            };
        }
        void IDatabaseInitializer<TContext>.InitializeDatabase(TContext context)
        {
            var doseed = !context.Database.Exists();
            // && new DatabaseTableChecker().AnyModelTableExists(context);
            // check to see if to seed - we 'lack' the 'AnyModelTableExists'
            // ...could be copied/done otherwise if needed...
    
            var migrator = new DbMigrator(_configuration);
            // if (doseed || !context.Database.CompatibleWithModel(false))
            if (migrator.GetPendingMigrations().Any())
                migrator.Update();
    
            // move on with the 'CreateDatabaseIfNotExists' for the 'Seed'
            base.InitializeDatabase(context);
            if (doseed)
            {
                Seed(context);
                context.SaveChanges();
            }
        }
        protected override void Seed(TContext context)
        {
        }
    }
    
    1) what is the correct use-case for the different techniques available ?
    
    2) what would be the ideal strategy so that the migrations work in all conditions 
    and when we move databases from one environment to another ?
    
     Database.SetInitializer(new DomainInitializer());