EF中IDatabaseInitializer的正確用法是什麼?

c# code-first-migrations ef-code-first entity-framework entity-framework-6

我有一個自定義DatabaseInitialiser,如下所示

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

關於上述代碼的內容不僅僅是hacky(隨意提供幫助)

然後我添加了遷移並使遷移腳本也正常工作。

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

遷移按預期工作。

現在,問題出在我的應用程序啟動中,我該怎麼辦?像這樣的東西

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

一些論壇也在構造函數中提出了這一點,這看起來有點奇怪,因為我不想每次使用Context時都檢查數據庫和模式是否正確。那麼,這種技術的可能用途是什麼?或者我是否認為該建議的背景是錯誤的?

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

總結一下,

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

對於切換連接時,請參閱我關於問題的帖子
在不使用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<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 ?

退一步問自己何時希望EF進行初始化和遷移。在我的(企業)組織中,我們對如何遷移代碼,數據和DDL有非常嚴格的規則,因此這些功能對我們沒有任何價值。即使我們可以使用它們,我也會非常謹慎,直到我在不同的環境中擁有相當多的工具經驗。

那麼,為什麼要用呢?您正在開始一個新的綠色領域項目,並且正在使用代碼優先。現在,這是有用的地方。您可以編寫代碼,編譯,運行,讓EF擔心新的或缺少的字段,關係和表。稍後,您可以在開發服務器中對其進行形式化,然後永遠刪除初始化程序和遷移程序。

至於如何設置?聽起來你找到了一種有效的方法。我同意它似乎有點hacky,但代碼類似於我的初始設置。我正在使用MVC.NET,所以我在Global.asax.cs中使用了以下內容:

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 ?

底線是我認為你的(2)是錯誤的問題。應該是,“我真的想這樣做嗎?”。你可能會這樣做,但我會首先考慮其他更傳統的選擇,特別是如果你在一個超過10人的公司。




許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因