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

我有一个自定义DatabaseInitialiser,如下所示

///  /// Implements the IDatabaseInitializer to provide a custom database initialisation for the context. ///  /// TContext is the DbContext public class ParikshaDataBaseInitializer : IDatabaseInitializer where TContext : DbContext { ///  /// 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. ///  /// The DbContext on which to run the initialiser 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(); } } } 

关于上述代码的内容不仅仅是hacky(随意提供帮助)

然后我添加了迁移并使迁移脚本也正常工作。

  internal sealed class Configuration : DbMigrationsConfiguration { public Configuration() { AutomaticMigrationsEnabled = false; ContextKey = "EFRepository.Context.ParikshaContext"; } protected override void Seed(ParikshaContext context) { } } 

迁移按预期工作。

现在,问题出在我的应用程序启动中,我该怎么办? 像这样的东西

  var config = new Configuration(); var migrator = new DbMigrator(config); migrator.Update(); 

一些论坛也在构造函数中提出了这一点,这看起来有点奇怪,因为我不想每次使用Context时都检查数据库和模式是否正确。 那么,这种技术的可能用途是什么?或者我是否认为该建议的背景是错误的?

 public ParikshaContext() : base("Pariksha") { Database.SetInitializer(new ParikshaDataBaseInitializer()); } 

总结一下,

  1. 什么是可用的不同技术的正确用例?

  2. 什么是理想的策略,以便迁移在所有条件下工作,以及何时将数据库从一个环境移动到另一个环境?

这是我在Db Initializer上的尝试,它结合了Migration初始化程序和默认的Db Create with with seeding。 (注意:它不是理想的,更像是一个简单的练习,但是给出了你在这里要求的解决方案,主要是工作 – 只需检查我所做的所有更新)。

如何创建初始化程序来创建和迁移mysql数据库?

至于why以及how – 完全理解我建议你也参考EF源代码 (这是新版本,但在许多方面类似)

1)

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

所以把它放在构造函数中是完全安全的(虽然我更喜欢在某个地方启动它,配置也是如此)。 它只在需要初始化时被调用 (并且使用了last one set ),您不应该手动调用它。

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

对于切换连接时,请参阅我关于问题的post
在不使用IDbContextFactory的情况下编写第一个自定义连接字符串和迁移代码

b)如果您创建自己的IDatabaseInitializer并且仍希望迁移能够side by side工作

你不应该只是从外面调用DbMigrator – 因为你的自定义初始化程序将错过整个’数据库创建’(例如,如果你想种子或其他东西 – 请查看上面的例子)。

这两件事都是有效的“初始化者” – 所以你需要将它们整合到一起 ,以某种方式chain事物。 请记住order of execution很重要(参见上面的问题) – 你应该检查’空状态’,然后调用DbMigrator ,然后进行自己的初始化。 我使用一个初始化程序作为基类,并合并另一个。

如果你只想seed – 你可以使用迁移配置,这是最简单的,如果合理的话。

2)

是非常“开放式”,并没有单一的答案。 通常它有效,但问题被解雇了……

  • 迁移是3件事(我认为) – 你的代码模型/实体,你的数据库/表和Db中的__MigrationHistory系统表。 所有3个都需要保持in sync 。 如果您“不同步”,您可以删除迁移表,重新创建迁移(使用标记来保留现有数据库),然后继续前进 – 即有实时数据的解决方案。 为此,请参阅如何忽略EF 4.3迁移中的表/类 ,

  • 在移动数据库时你需要删除/创建Db的权限,

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

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

  • 不要mix database / code版本 – 即一个代码实体版本 – 一个数据库。 如果你想与不同的代码版本共享相同的Db(例如分期,生产) – 不要(EF6中可以使用多租户解决方案 – 例如这个 ),

  • 如果你需要手动应用数据库 – 通过Update-Database生成script – 并应用它,不要手动操作或者你会弄错(迁移历史表) – 请看这个 ,

……这只是为数不多的。 它是相当稳定和可用的IMO – 但如果你遵守规则 – 并知道限制是什么。


 class CreateAndMigrateDatabaseInitializer : CreateDatabaseIfNotExists, IDatabaseInitializer where TContext : DbContext where TConfiguration : DbMigrationsConfiguration, 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.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 ? 

退一步问自己何时希望EF进行初始化和迁移。 在我的(企业)组织中,我们对如何迁移代码,数据和DDL有非常严格的规则,因此这些function对我们没有任何价值。 即使我们可以使用它们,我也会非常谨慎,直到我在不同的环境中拥有相当多的工具经验。

那么,为什么要用呢? 您正在开始一个新的绿色领域项目,并且正在使用代码优先。 现在,这是有用的地方。 您可以编写代码,编译,运行,让EF担心新的或缺少的字段,关系和表。 稍后,您可以在开发服务器中对其进行forms化,然后永远删除初始化程序和迁移程序。

至于如何设置? 听起来你找到了一种有效的方法。 我同意它似乎有点hacky,但代码类似于我的初始设置。 我正在使用MVC.NET,所以我在Global.asax.cs中使用了以下内容:

  Database.SetInitializer(new DomainInitializer()); 

底线是我认为你的(2)是错误的问题。 应该是,“我真的想这样做吗?”。 你可能会这样做,但我会首先考虑其他更传统的选择,特别是如果你在一个超过10人的公司。