25

コード ファースト マイグレーション機能の使用方法がわかりません。私の理解では、データベースがまだ存在しない場合は作成し、移行ファイルに従って最新のスキーマに更新する必要があります。しかし、常に多くのエラーが発生し、これを適切に使用する方法が全体的にわからないため、苦労しています..

internal class Program
{
    private static void Main()
    {
        EntityFrameworkProfiler.Initialize();

        Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
        Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Migrations.Configuration>());

        using (var context = new MyContext())
        {
            var exists = context.Database.Exists();
            if (!exists)
            {
                context.Database.Create();
            }

            var element = context.Dummies.FirstOrDefault();
        }
    }
}

public class MyContext : DbContext
{
    public MyContext()
        : base(string.Format(@"DataSource=""{0}""", @"C:\Users\user\Desktop\MyContext.sdf"))
    {
    }

    public DbSet<Dummy> Dummies { get; set; }
}

internal sealed class Configuration : DbMigrationsConfiguration<MyContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = true;
    }

    protected override void Seed(CodeFirstTest.MyContext context)
    {
    }
}

Entity Framework Profiler を使用して、実行されるステートメントを確認します。データベースが存在しない状態でプログラムを実行すると、次の出力が得られます。

-- ステートメント #1 SELECT [GroupBy1].[A1] AS [C1] FROM (SELECT COUNT(1) AS [A1] FROM [__MigrationHistory] ​​AS [Extent1]) AS [GroupBy1]

-- ステートメント #2 警告: System.Data.SqlServerCe.SqlCeException (0x80004005): 指定されたテーブルが存在しません。[ __MigrationHistory ] System.Data.SqlServerCe.SqlCeCommand.ProcessResults(Int32 hr) で System.Data.SqlServerCe.SqlCeCommand.CompileQueryPlan() で System.Data.SqlServerCe.SqlCeCommand.ExecuteCommand(CommandBehavior 動作、文字列メソッド、ResultSetOptions オプション) でSystem.Data.SqlServerCe.SqlCeCommand.ExecuteReader(CommandBehavior の動作)、System.Data.SqlServerCe.SqlCeMultiCommand.ExecuteReader(CommandBehavior の動作)、HibernatingRhinos.Profiler.Appender.ProfiledDataAccess でProfiledCommand.ExecuteDbDataReader (CommandBehavior 動作)

-- ステートメント #3 SELECT [GroupBy1].[A1] AS [C1] FROM (SELECT COUNT(1) AS [A1] FROM [__MigrationHistory] ​​AS [Extent1]) AS [GroupBy1]

-- ステートメント #4 警告: System.Data.SqlServerCe.SqlCeException (0x80004005): 指定されたテーブルが存在しません。[ __MigrationHistory ] System.Data.SqlServerCe.SqlCeCommand.ProcessResults(Int32 hr) で System.Data.SqlServerCe.SqlCeCommand.CompileQueryPlan() で System.Data.SqlServerCe.SqlCeCommand.ExecuteCommand(CommandBehavior 動作、文字列メソッド、ResultSetOptions オプション) でSystem.Data.SqlServerCe.SqlCeCommand.ExecuteReader(CommandBehavior の動作)、System.Data.SqlServerCe.SqlCeMultiCommand.ExecuteReader(CommandBehavior の動作)、HibernatingRhinos.Profiler.Appender.ProfiledDataAccess でProfiledCommand.ExecuteDbDataReader (CommandBehavior 動作)

-- ステートメント #5 SELECT [GroupBy1].[A1] AS [C1] FROM (SELECT COUNT(1) AS [A1] FROM [__MigrationHistory] ​​AS [Extent1]) AS [GroupBy1]

-- ステートメント 6 警告: System.Data.SqlServerCe.SqlCeException (0x80004005): 指定されたテーブルが存在しません。[ __MigrationHistory ] System.Data.SqlServerCe.SqlCeCommand.ProcessResults(Int32 hr) で System.Data.SqlServerCe.SqlCeCommand.CompileQueryPlan() で System.Data.SqlServerCe.SqlCeCommand.ExecuteCommand(CommandBehavior 動作、文字列メソッド、ResultSetOptions オプション) でSystem.Data.SqlServerCe.SqlCeCommand.ExecuteReader(CommandBehavior の動作)、System.Data.SqlServerCe.SqlCeMultiCommand.ExecuteReader(CommandBehavior の動作)、HibernatingRhinos.Profiler.Appender.ProfiledDataAccess でProfiledCommand.ExecuteDbDataReader (CommandBehavior 動作)

-- ステートメント #7 SELECT [GroupBy1].[A1] AS [C1] FROM (SELECT COUNT(1) AS [A1] FROM [__MigrationHistory] ​​AS [Extent1]) AS [GroupBy1]

-- ステートメント 8 警告: System.Data.SqlServerCe.SqlCeException (0x80004005): 指定されたテーブルが存在しません。[ __MigrationHistory ] System.Data.SqlServerCe.SqlCeCommand.ProcessResults(Int32 hr) で System.Data.SqlServerCe.SqlCeCommand.CompileQueryPlan() で System.Data.SqlServerCe.SqlCeCommand.ExecuteCommand(CommandBehavior 動作、文字列メソッド、ResultSetOptions オプション) でSystem.Data.SqlServerCe.SqlCeCommand.ExecuteReader(CommandBehavior の動作)、System.Data.SqlServerCe.SqlCeMultiCommand.ExecuteReader(CommandBehavior の動作)、HibernatingRhinos.Profiler.Appender.ProfiledDataAccess でProfiledCommand.ExecuteDbDataReader (CommandBehavior 動作)

-- ステートメント #9 分離レベルでトランザクションを開始: Serializable

-- ステートメント #10 CREATE TABLE [Dummies] ( [Name] nvarchar NOT NULL, CONSTRAINT [PK_Dummies] PRIMARY KEY ([Name]) )

-- ステートメント #11 CREATE TABLE [ MigrationHistory] ​​( [MigrationId] nvarchar NOT NULL, [CreatedOn] [datetime] NOT NULL, [Model] [image] NOT NULL, [ProductVersion] nvarchar NOT NULL, CONSTRAINT [PK _MigrationHistory] ​​PRIMARY KEY ([移行 ID]) )

-- ステートメント #12 INSERT INTO [__MigrationHistory] ​​([MigrationId], [CreatedOn], [Model], [ProductVersion]) VALUES ('201207261524579_InitialCreate', '2012-07-26T15:24:58.523', 0x1F8B080 , '4.3. 1')

-- ステートメント #13 commit トランザクション

-- ステートメント #14 SELECT TOP (1) [c].[名前] AS [名前] FROM [ダミー] AS [c]

ご覧のとおり、実際にデータベースを作成する前に、データベースに4回アクセスしようとしています。これは正しくないようです。既存のデータベースでアプリケーションを起動すると、実際のクエリが実行される前に、データベースに対して 7 回クエリが実行されます。context.Database.Create()これはではなく で発生することに注意してください.Exists()

また、構成のシード メソッドは呼び出されませんが、コンストラクターは呼び出されます。

これはすべて非常に間違っていて混乱しているようです。最初にエラーが頻繁に発生する理由と、シードメソッドがまったく呼び出されない理由を誰かが教えてくれることを願っています。

SqlServer コンパクトと Entity Framework の最新の安定バージョンを使用しています。

パッケージ id="EntityFramework" バージョン="4.3.1" targetFramework="net40"

パッケージ id="Microsoft.SqlServer.Compact" バージョン="4.0.8854.2" targetFramework="net40"

4

3 に答える 3

12

エンティティフレームワークを構成するには多くの方法があるようで、誰もが最善のものを独自に取り入れています。私が提供できるのは、私の仕事で標準化したものに基づいた私の見方だけです. これの多くは開発者の好みです。私の好みはたまたま可能な限り制御することなので、いつ何が起こっているかを正確に理解しています。

自動移行

まず、自動移行は便利かもしれませんが、特にプロジェクトが大きくなったり、データが複雑になったりすると、多くの問題が発生します。私の意見では、商用/生産システムはこれよりも詳細に制御する必要があります。を設定することにより、すべての主要なプロジェクトの自動移行を常にオフにしていますAutomaticMigrationsEnabled = false;。必要に応じて移行を明示的に実行します (開発では、これは Visual Studio のパッケージ マネージャー コンソールに入力Update-Databaseして実行します。本番環境では、最新のコードへの移行を明示的に呼び出すだけの独自の小さな移行ユーティリティを作成しましたが、どれも自動ではありません)。

@Terricの答えは、自動移行とデータ損失の両方が許可されていることで私を怖がらせます! 私は、データの損失につながる列の変更が不適切に実行されたために、ソリューションを展開し、いくつかの重要なデータを消去する人にはなりたくありません。ちなみに、dev で移行を明示的に実行するときは、詳細な出力 ( ) に -v スイッチを使用することがよくありますUpdate-Database -v。これにより、実行中の SQL と、必要に応じてエラー/警告を確認できます。

また、開発環境への移行を数回行った後にこれらの設定を変更するとうまくいかないという経験もあります。これがどこで追跡されているのかはわかりませんが、自動移行を無効にしてプロジェクトを新たに開始すると、予期しないことが起こらないことが保証されます.

個人的には、あなたが持っているイニシャライザーを削除し、必要なMigrateDatabaseToLatestVersionときにマイグレーターを自分で実行します (パッケージマネージャーコンソールまたはどこかの明示的なコードを介して)。

データベースが存在しない場合の作成

この動作は、DatabaseInitializer (実際には EntityFramework 自体ではありません) によって提供されます。イニシャライザはCreateDatabaseIfNotExistsEntityFramework に組み込まれており、一部のバージョンではデフォルトです。ただし、繰り返しますが、私はすべての推測されたアプリの動作に関するものではありません。私の意見では、もう少しコントロールしたいと思います。

この男には、組み込みの CreateDatabaseIfNotExists から継承するカスタム データベース初期化子の例があります。ただし、いつでも独自のロジックを作成して、見たい正確なロジックを実装することができます (データベースの作成を含む)。繰り返しますが、これは予期しない動作を回避するだけです。開発者としての私の個人的な好みは、モックアップやテスト プロジェクトでふざけているだけでない限り、このようなことを厳密に制御することです。

予期しない動作のない非常にシンプルなカスタム DatabaseInitializer:

namespace MyProject.Data.DatabaseInitializers
{
    public class MyCustomDbInit<TContext> : IDatabaseInitializer<TContext>
        where TContext : DbContext
    {
        public void InitializeDatabase(TContext context)
        {
            // Create our database if it doesn't already exist.
            context.Database.CreateIfNotExists()

            // Do you want to migrate to latest in your initializer? Add code here!

            // Do you want to seed data in your initializer? Add code here!
        }
    }
}

結果

コード ファーストのアプローチを使用し、自動移行を無効にして、上記のようなカスタム DatabaseInitializer を使用すると、何がいつ発生するかを非常に適切に制御できます。

私たちはこれらの戦略を職場で使用しており、問題はありません (ただし、これらの戦略に落ち着くまでには多少の手間がかかりました)。うまくいけば、あなたも同様の成功を収めるでしょう!

于 2012-08-10T17:50:54.407 に答える
5

上記のコードを使用して最初に EF コードを使用するだけでなく、SQL CE を使用して問題を再現することができました。

奇妙なことに、あなたのコードをそのまま使用したとき、初めて完全に機能しました。問題が発生するためには_MigrationHistory、.sdf ファイルのテーブルを実際に削除する必要がありました。

.sdf ファイルを削除することによって (これはあなたのケースではオプションではないかもしれませんが、それについては後で説明します) 次回の実行時に、移行テーブルが作成されましたが、それでも適切に機能しませんでした。お気づきの場合は、ステップ 12 で、最終的にテーブルが作成されます。

-- ステートメント #1 SELECT [GroupBy1].[A1] AS [C1] FROM (SELECT COUNT(1) AS [A1] FROM [Dummies] AS [Extent1]) AS [GroupBy1]

-- ステートメント #2 SELECT [GroupBy1].[A1] AS [C1] FROM (SELECT COUNT(1) AS [A1] FROM [__MigrationHistory] ​​AS [Extent1]) AS [GroupBy1]

-- ステートメント #3 警告: System.Data.SqlServerCe.SqlCeException (0x80004005): 指定されたテーブルが存在しません。[ __MigrationHistory ]

-- ステートメント #4 SELECT [GroupBy1].[A1] AS [C1] FROM (SELECT COUNT(1) AS [A1] FROM [__MigrationHistory] ​​AS [Extent1]) AS [GroupBy1]

-- ステートメント 5 警告: System.Data.SqlServerCe.SqlCeException (0x80004005): 指定されたテーブルが存在しません。[ __MigrationHistory ]

-- ステートメント #6 SELECT [GroupBy1].[A1] AS [C1] FROM (SELECT COUNT(1) AS [A1] FROM [__MigrationHistory] ​​AS [Extent1]) AS [GroupBy1]

-- ステートメント 7 警告: System.Data.SqlServerCe.SqlCeException (0x80004005): 指定されたテーブルが存在しません。[ __MigrationHistory ]

-- ステートメント #8 SELECT [GroupBy1].[A1] AS [C1] FROM (SELECT COUNT(1) AS [A1] FROM [__MigrationHistory] ​​AS [Extent1]) AS [GroupBy1]

-- ステートメント 9 警告: System.Data.SqlServerCe.SqlCeException (0x80004005): 指定されたテーブルが存在しません。[ __MigrationHistory ]

-- ステートメント #10 分離レベルでトランザクションを開始: Serializable

-- ステートメント #11 CREATE TABLE [Dummy] ( [DummyId] [int] NOT NULL IDENTITY, [test] nvarchar, [addThis] nvarchar, CONSTRAINT [PK_Dummies] PRIMARY KEY ([DummyId]) )

-- ステートメント #12 CREATE TABLE [__MigrationHistory] ​​( [MigrationId] nvarchar NOT NULL, [CreatedOn] [datetime] NOT NULL, [Model] [image] NOT NULL, [ProductVersion] nvarchar NOT NULL, CONSTRAINT [PK___MigrationHistory] ​​PRIMARY KEY ( [移行 ID]) )

-- ステートメント #13 INSERT INTO [__MigrationHistory] ​​([MigrationId], [CreatedOn], [Model], [ProductVersion]) VALUES ('201208101940587_InitialCreate', '2012-08-10T19:40:59.055',0x1F8B0800000000000400CD57DD8ED33C10BDFF24DE21F23D75591082550A5ADA5D5441771159B89FC6D3D6C27642ECACDA67FB2E78245E8171FE9A26FDD95D40E2AE998CCF1C1F9FF1A43FFFFF11BE5D6B15DC6166656246ECD960C802347122A4598E58EE164F5FB1B76F9EFC175E0ABD0EBED679673E8F561A3B622BE7D273CE6DBC420D76A0659C253659B8419C680E22E167C3E12BFE6CC8912018610541F839374E6A2C1EE8719C98185397839A250295ADE2F4262A50836BD068538871C4DE8195F198D2AE64661D0B2E94046211A15A3C90D2F0B5A7C49A6254EE9268B9CDED26C5A2E4884D72AD37ED144AFA809B9D00853E65498A99DB7CC6457BE154B080EF2EE6DDD5CDDAEE42CF62C4A6C63D3F63C175AE14CC150516A02CB2207D791EB924C3F768300387E2133887199DCD5460B18B4A8DF3F4E5FD0479CD87675E100EC6240E1C1D748F7D87AB437F0225D1C865E419165CC9358A8F68966ED5909DC1BA8EBC180EC93A5F8C248F79842CC7F6EECAE7E3554188DB95B47FBB70C8B76EE87B842CEB4092F615A95911C1B5EB98A5CC8ED0B58E572291DF8297E61D9456DB4BA129B66D0C5E7646DD41FC400B8533485392A7D552552488CA7E1A3F8D1EDE37BAC4E0B1DDD33E0DDBA612191596D8794BA5EB2E9E808339F8031B0BDD4B3B296D5D6757E16E8F6E05AFF3FDEF72CDEEA532D8D3F56DE9AE68379A9AACD8183634F6DE16D5CA280605D9A1561F272AD7E6E8C5710CA76CC3364819B93F42D3526D9026D8C70979478AAED8BCA776E71AEC1EDE31D777539AEA8DFB3B2E0F2BC79D9E263D0B96292C2081EEA4F0F68B36D6A11EF88441F45D4598D1B81CE3E0859F8175DA0C8C5C90E8B7C937A43BB878F7D8C9D45CC4D60AF58F8F2769C8672707516F7ADD7FAC983BC8E215647BEEF2DF9A1A8FC47DD850E85F70A787C3E6F06828BD3962629ED0364A8ACD3C79E4DCE8774AC8DB5F67E104AD5C6E21FCB79AC1D87F1D6C41EB9CA95924B5ECB4AB36A33AA5732A337420489C8BCCC905C48E5EC7686D31D0BF82CA29E552CF514CCD4DEED2DC5D588B7AAE76E664C88FD72F86E32EE7F026F54FF64F6C81684ADA02DE9877B954A2E17DD5B7FA2108EF93AA7F88157DD010DC72D3205D27E69E40957C134CD1F8EEBB459D2A02B33726823B3CCCEDB486BB8A851309CB0C745BC132523189802AB74A5081F68A6D3DFFE782FB7F176F7E018E4F04B08F0C0000、「4.3.1」)

-- ステートメント #14 commit トランザクション

-- ステートメント #15 SELECT [GroupBy1].[A1] AS [C1] FROM (SELECT COUNT(1) AS [A1] FROM [Dummies] AS [Extent1]) AS [GroupBy1]

適切な場所にテーブルを作成することで問題を解決します。

ここに画像の説明を入力

これを行うと、次にコードを実行したときに、すべてが再び完全に実行されました。

-- ステートメント #1 SELECT [GroupBy1].[A1] AS [C1] FROM (SELECT COUNT(1) AS [A1] FROM [__MigrationHistory] ​​AS [Extent1]) AS [GroupBy1]

-- ステートメント #2 SELECT TOP (1) [c].[ProductVersion] AS [ProductVersion] FROM [__MigrationHistory] ​​AS [c]

-- ステートメント #3 SELECT [GroupBy1].[A1] AS [C1] FROM (SELECT COUNT(1) AS [A1] FROM [__MigrationHistory] ​​AS [Extent1]) AS [GroupBy1]

-- ステートメント #4 SELECT [Extent1].[MigrationId] AS [MigrationId] FROM [__MigrationHistory] ​​AS [Extent1]

-- ステートメント #5 SELECT [GroupBy1].[A1] AS [C1] FROM (SELECT COUNT(1) AS [A1] FROM [__MigrationHistory] ​​AS [Extent1]) AS [GroupBy1]

-- ステートメント #6 SELECT [GroupBy1].[A1] AS [C1] FROM (SELECT COUNT(1) AS [A1] FROM [__MigrationHistory] ​​AS [Extent1]) AS [GroupBy1]

-- ステートメント #7 SELECT TOP (1) [Project1].[C1] AS [C1], [Project1].[MigrationId] AS [MigrationId], [Project1].[Model] AS [Model] FROM (SELECT [Extent1 ].[MigrationId] AS [MigrationId], [Extent1].[CreatedOn] AS [CreatedOn], [Extent1].[Model] AS [Model], 1 AS [C1] FROM [__MigrationHistory] ​​AS [Extent1]) AS [ Project1] ORDER BY [Project1].[CreatedOn] DESC

-- ステートメント #8 SELECT [GroupBy1].[A1] AS [C1] FROM (SELECT COUNT(1) AS [A1] FROM [__MigrationHistory] ​​AS [Extent1]) AS [GroupBy1]

-- ステートメント #9 SELECT TOP (1) [Project1].[C1] AS [C1], [Project1].[MigrationId] AS [MigrationId], [Project1].[Model] AS [Model] FROM (SELECT [Extent1 ].[MigrationId] AS [MigrationId], [Extent1].[CreatedOn] AS [CreatedOn], [Extent1].[Model] AS [Model], 1 AS [C1] FROM [__MigrationHistory] ​​AS [Extent1]) AS [ Project1] ORDER BY [Project1].[CreatedOn] DESC

-- ステートメント #10 分離レベル: 未指定でトランザクションを開始

-- ステートメント #11 挿入 [ダミー] ([テスト]、[addThis]) 値 (null、null);

[DummyId] から [DummyId] を選択 ([DummyId] = @@IDENTITY)

-- ステートメント #12 挿入 [ダミー] ([テスト]、[addThis]) 値 (null、null);

[DummyId] から [DummyId] を選択 ([DummyId] = @@IDENTITY)

-- ステートメント 13 挿入 [ダミー] ([テスト]、[addThis]) 値 (null、null);

[DummyId] から [DummyId] を選択 ([DummyId] = @@IDENTITY)

-- ステートメント #14 挿入 [ダミー] ([テスト]、[addThis]) 値 (null、null);

[DummyId] から [DummyId] を選択 ([DummyId] = @@IDENTITY)

-- ステートメント #15 commit トランザクション

-- ステートメント #16 SELECT [GroupBy1].[A1] AS [C1] FROM (SELECT COUNT(1) AS [A1] FROM [Dummies] AS [Extent1]) AS [GroupBy1]

SDF 全体を削除して再作成することができない場合は、次のようにして元に戻します。

appconfig で接続文字列を作成します (おそらく動的接続文字列が必要なのだと思いますが、これがコード内にある理由ですが、これ1 回限りのことです)。私の接続文字列は次のようになりました。

   <connectionStrings>
        <add connectionString="Data Source=MyContext.sdf;Persist Security Info=False;" name="MyContext" providerName="System.Data.SqlServerCe.4.0"/>
    </connectionStrings>

Context のコンストラクターを変更して、接続文字列を使用するようにします。

public MyContext()
    //: base(string.Format(@"DataSource=""{0}""", "MyContext.sdf"))
    : base("MyContext")

これはすべて、Package Manager Console でいくつかのコマンドを実行してテーブルを再作成できるようにするために必要です。パッケージ マネージャー コンソールを開き、次のコマンドを実行します。

add-migration initial -ignorechanges

次に、プログラムを実行します。いくつかの警告がスローされますが、その後、テーブルが作成され、データが入力されます。その後、コンストラクターを元に戻すことができ、問題はなくなりました。

注:それが機能し始めると、シード関数も機能し始めました

于 2012-08-10T20:32:48.430 に答える
1

私はあなたが提供したコードを試してみましたが、この場合は (CE の代わりに SQL Server を使用)、次のようになりました。Database.Create コードを削除し、EF の自動移行がそれを実行できるようにしました。これが実行され、シード メソッドが正しく呼び出されるようになりました。

internal class Program
{
    private static void Main()
    {
        EntityFrameworkProfiler.Initialize();

        Database.DefaultConnectionFactory = new SqlConnectionFactory("System.Data.SqlServer");
        Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, MyContextConfiguration>());

        using (var context = new MyContext())
        {
            var element = context.Dummies.FirstOrDefault();
        }
    }
}

internal class Dummy
{
    public String Id { get; set; }
}

internal sealed class MyContext : DbContext
{
    public MyContext() : base(@"Data Source=localhost;Initial Catalog=Dummies;User Id=<USER_ID>;Password=<PASSWORD>;MultipleActiveResultSets=False;")
    {
    }

    public DbSet<Dummy> Dummies { get; set; }
}

internal sealed class MyContextConfiguration : DbMigrationsConfiguration<MyContext>
{
    public MyContextConfiguration()
    {
        AutomaticMigrationsEnabled = true;
        AutomaticMigrationDataLossAllowed = true;
    }

    protected override void Seed(MyContext context)
    {
        context.Dummies.AddOrUpdate(new Dummy() { Id = "First" });
    }
}

EF プロファイラーを見ると、現在 DB に対して実行されているクエリが増えていることがわかります (古い EdmMetaData テーブルのチェックさえあります...これは非常に奇妙なことです。 __MigrationHistory テーブルの)。なぜこれが起こっているのかわかりません。それは私たちの側の構成の問題 (修正方法はまだわかりません) か、移行コードのバグのどちらかだと思います。

したがって、EF の移行では、コード ベースの移行 (こちらのブログ投稿を参照) または自動移行 (このコード スニペットが示すように)のいずれかに任されていると思います。時間が経つにつれて、EF (または移行方法) がこの奇妙な動作をする理由をよりよく理解できるようになると思います。または、EF 自体が進化するにつれて改善されるでしょう。

于 2012-07-30T12:03:47.977 に答える