4

Entity Framework コード ファーストでのデータ モデルの目的について、私は少し混乱しています。データベースがまだ存在しない場合、EF はデータベースをゼロから自動生成するため、データ モデル (データ注釈と .NET の Fluent API を含むDbContext.OnModelCreating) のみを使用します。データ モデルはデータベースの構造を完全に記述する必要があると想定していました。 、その後は基本的なものを変更する必要はありません。

しかし、EF Triage チームのメンバーの 1 人が、データ モデル フィールドや Fluent API コードへの注釈としてではなく、データ移行にカスタム インデックスを追加することを提案しているこの Codeplex の問題に遭遇しました。

しかし、それは、データベースをゼロから自動生成する人が、それらのカスタム インデックスを自分の DB に追加できないということではないでしょうか? データ移行の使用を開始すると、データベースを最初から作成することは決してないと思われます。チームで作業していて、新しいチーム メンバーが新しい SQL Server のインストールと共にやってきたらどうしますか? 他のチーム メンバーからデータベースをコピーすることは想定されていますか? Postgres のような新しい DBMS の使用を開始したい場合はどうすればよいでしょうか? EF の優れた点の 1 つは、それが DBMS に依存しないことだと思いましたが、データベースをゼロから作成できなければ、DBMS に依存しない方法で作業を行うことはできなくなります。

上記で概説した理由から、カスタム インデックスをデータ モデルではなくデータ移行に追加することは悪い考えではないでしょうか? さらに言えば、DB 構造の変更を移行に追加するが、データ モデルには追加しないのは悪い考えではないでしょうか?

4

2 に答える 2

3

EF コード ファースト モデルは、データベースの構造を完全に記述することを目的としていますか?

いいえ、データベースの構造やスキーマを完全には記述していません。それでも、EF を使用してデータベースを完全に記述する方法があります。それらは以下のとおりです。

Database クラスで新しい CTP5 のExecuteSqlCommandメソッドを使用すると、生の SQL コマンドをデータベースに対して実行できます。

SqlCommandこの目的でメソッドを呼び出すのに最適な場所は、カスタムクラスでオーバーライドさSeedれたメソッド内です。例えば:Initializer

protected override void Seed(EntityMappingContext context)
{
    context.Database.ExecuteSqlCommand("CREATE INDEX IX_NAME ON ...");
}

この方法で一意の制約を追加することもできます。これは回避策ではありませんが、データベースが生成されるときに適用されます。

また

あなたが属性をひどく必要としているなら、ここに行きます

[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
public class IndexAttribute : Attribute
{
    public IndexAttribute(string name, bool unique = false)
    {
        this.Name = name;
        this.IsUnique = unique;
    }

    public string Name { get; private set; }

    public bool IsUnique { get; private set; }
}

OnModelCreatingこの後、以下のようにメソッドで呼び出すイニシャライザがあります。

public class IndexInitializer<T> : IDatabaseInitializer<T> where T : DbContext
{
    private const string CreateIndexQueryTemplate = "CREATE {unique} INDEX {indexName} ON {tableName} ({columnName});";

    public void InitializeDatabase(T context)
    {
        const BindingFlags PublicInstance = BindingFlags.Public | BindingFlags.Instance;
        Dictionary<IndexAttribute, List<string>> indexes = new Dictionary<IndexAttribute, List<string>>();
        string query = string.Empty;

        foreach (var dataSetProperty in typeof(T).GetProperties(PublicInstance).Where(p => p.PropertyType.Name == typeof(DbSet<>).Name))
        {
            var entityType = dataSetProperty.PropertyType.GetGenericArguments().Single();
            TableAttribute[] tableAttributes = (TableAttribute[])entityType.GetCustomAttributes(typeof(TableAttribute), false);

            indexes.Clear();
            string tableName = tableAttributes.Length != 0 ? tableAttributes[0].Name : dataSetProperty.Name;

            foreach (PropertyInfo property in entityType.GetProperties(PublicInstance))
            {
                IndexAttribute[] indexAttributes = (IndexAttribute[])property.GetCustomAttributes(typeof(IndexAttribute), false);
                NotMappedAttribute[] notMappedAttributes = (NotMappedAttribute[])property.GetCustomAttributes(typeof(NotMappedAttribute), false);
                if (indexAttributes.Length > 0 && notMappedAttributes.Length == 0)
                {
                    ColumnAttribute[] columnAttributes = (ColumnAttribute[])property.GetCustomAttributes(typeof(ColumnAttribute), false);

                    foreach (IndexAttribute indexAttribute in indexAttributes)
                    {
                        if (!indexes.ContainsKey(indexAttribute))
                        {
                            indexes.Add(indexAttribute, new List<string>());
                        }

                        if (property.PropertyType.IsValueType || property.PropertyType == typeof(string))
                        {
                            string columnName = columnAttributes.Length != 0 ? columnAttributes[0].Name : property.Name;
                            indexes[indexAttribute].Add(columnName);
                        }
                        else
                        {
                            indexes[indexAttribute].Add(property.PropertyType.Name + "_" + GetKeyName(property.PropertyType));
                        }
                    }
                }
            }

            foreach (IndexAttribute indexAttribute in indexes.Keys)
            {
                query += CreateIndexQueryTemplate.Replace("{indexName}", indexAttribute.Name)
                .Replace("{tableName}", tableName)
                .Replace("{columnName}", string.Join(", ", indexes[indexAttribute].ToArray()))
                .Replace("{unique}", indexAttribute.IsUnique ? "UNIQUE" : string.Empty);
            }
        }

        if (context.Database.CreateIfNotExists())
        {
            context.Database.ExecuteSqlCommand(query);
        }
    }

    private string GetKeyName(Type type)
    {
        PropertyInfo[] propertyInfos = type.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public);
        foreach (PropertyInfo propertyInfo in propertyInfos)
        {
            if (propertyInfo.GetCustomAttribute(typeof(KeyAttribute), true) != null)
            return propertyInfo.Name;
        }
        throw new Exception("No property was found with the attribute Key");
    }
}

次にOnModelCreating、DbContext でオーバーロードします

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    Database.SetInitializer(new IndexInitializer<MyContext>());
    base.OnModelCreating(modelBuilder);
}

index 属性をエンティティ タイプに適用します。このソリューションを使用すると、同じ名前と一意の名前を使用するだけで、同じインデックスに複数のフィールドを含めることができます。

また

移行は後で行うことができます。

注: このコードの多くはhereから取得しました。

于 2013-02-16T18:07:42.887 に答える
0

問題は、移行を途中で追加することに価値があるかどうか、またはそれらが別のマシンでの将来のデータベースの初期化で問題を引き起こすかどうかです。

作成される最初の移行には、既存のデータ モデル全体も含まれているため、(enable-migrationsパッケージ マネージャー コンソールで) 移行を追加することで、実際には、データベースが後で適切に作成される組み込みメカニズムを作成することになります。他の開発者。

これを行っている場合は、データベースの初期化戦略を変更して、既存のすべての移行を実行することをお勧めします。EF が起動して、次の開発者のデータベースが同期されないようにするためです。

次のようなものが機能します。

Database.SetInitializer(new MigrateDatabaseToLatestVersion<YourNamespace.YourDataContext, Migrations.Configuration>());

いいえ、これは本質的に将来の作業/開発者に問題を引き起こすことはありません。移行は、データベースに対して実行される有効な SQL に変換されることに注意してください。スクリプト モードを使用して、作成した移行に基づいて DB の変更を行うために必要な TSQL を出力することもできます。

乾杯。

于 2013-02-16T18:15:20.827 に答える