1

両方とも継承階層を持つエンティティ間に1対1の関係を構成しようとしています。

サンプルとして、次の最初の継承チェーンについて考えてみましょう。

[Table("A")]
public abstract class A
{
    public Guid ID { get; set; }
    ...
}

[Table("AA")]
public class AA : A
{
    ...
}

[Table("AB")]
public class AB : A
{
    ...
}

次に、この2番目の継承チェーンについて考えます。

[Table("B")]
public abstract class B
{
    public Guid ID { get; set; }
}

[Table("BA")]
public class BA : B
{
    ...
}

[Table("BB")]
public class BB : B
{
    ...
}

AAを主要なエンティティとしてAAとBAの間に1対1の関係を追加します。

[Table("AA")]
public class AA : A
{
    ...
    public BA BAChild { get; set; }
    ...
}

[Table("BA")]
public class BA : B
{
    ...
    public AA Parent { get; set; }
    ...
}

public class AAConfiguration : EntityTypeConfiguration<AA>
{
    public AAConfiguration()
    {
        this.HasRequired(o => o.BAChild)
            .WithRequiredPrincipal(o => o.Parent);
    }
}

ABを主要なエンティティとしてABとBBの間に1対1の関係を追加します。

[Table("AB")]
public class AB : A
{
    ...
    public BB BBChild { get; set; }
    ...
}

[Table("BB")]
public class BB : B
{
    ...
    public AB Parent { get; set; }
    ...
}

public class ABConfiguration : EntityTypeConfiguration<AB>
{
    public ABConfiguration()
    {
        this.HasRequired(o => o.BBChild)
            .WithRequiredPrincipal(o => o.Parent);
    }
}

また、EFがエンティティAとBのテーブルを生成するようにしたいので、次の空のEntityTypeConfigurationを追加して登録しました。

public class AConfiguration : EntityTypeConfiguration<A>
{

}

public class BConfiguration : EntityTypeConfiguration<B>
{

}

このようなコードを実行すると、インデックスの作成中にバグが発生します(Entity Framework 4.3.1にアップグレードした後に未処理の例外が発生しました)

それでは、トリッキーなコードを実行して、SqlServerMigrationSqlGeneratorから派生したカスタムMigrationSqlGeneratorを登録して、ビジネスの命名規則に基づくインデックスの作成を回避しましょう。

public class Configuration : DbMigrationsConfiguration<DataContext>
{
    public Configuration()
    {
        ...
        this.SetSqlGenerator("System.Data.SqlClient", new CustomSqlServerGenerator());
        ...
    }
}

public class CustomSqlServerGenerator : SqlServerMigrationSqlGenerator
{
    protected override void Generate(CreateIndexOperation createIndexOperation)
    {
        if (createIndexOperation.Columns.Count() == 1 && createIndexOperation.Columns.Any(o => o == "ID"))
            return;

        base.Generate(createIndexOperation);
    }
}

public class DataContext : DbContext
{
    static DataContext()
    {
        Database.SetInitializer<DataContext>(new MigrateDatabaseToLatestVersion<DataContext, Configuration>());
    }

    ...
}

それでは、データベースを生成するときが来ました。データベースを作成するには、次のコードを使用します。

...
DataContext dataContext = new DataContext();
dataContext.Database.Initialize(true);
...

そして、生成されたデータベースを見ると、テーブルBAとBBの両方にテーブルABの外部キーがあり、BAとAAの間に外部キーがないことがわかります!?!

おそらく何かが足りないのですが、このサンプルの何が問題なのかわかりません。

データベースを適切に生成するために何ができるでしょうか?

4

1 に答える 1

1

リフレクター全体で詳細な検索を行った後、この動作はEntityFrameworkのバグのようです。

次の関数のSystem.Data.Entity.ModelConfiguration.Configuration.Mapping.ForeignKeyPrimitiveOperationsで問題を特定しました。

    private static void UpdatePrincipalTables(DbDatabaseMapping databaseMapping, DbTableMetadata toTable, bool removeFks, EdmAssociationType associationType, EdmEntityType et)
    {
        ...
    }

EntityFrameworkがモデルを読み込むと、DbDatabaseMappingインスタンスに基本型が読み込まれ、外部キー制約が基本型のメタデータに追加されます。EntityFrameworkは、外部キー制約の注釈にEdmAssociationTypeのインスタンスも格納して、正しい制約を生成するために必要なすべての情報を維持します。

後で、派生型がDbDatabaseMappingインスタンスに追加および構成されると、EntityFrameworkは関連付けの関連する端を更新して、派生テーブルの外部キー制約を移動しようとします。外部キー制約を識別して更新するには、次のセレクターを使用します。ここで、tableInfo.Valueは、外部キーの依存列を含むIEnumerableです。

fk => fk.DependentColumns.SequenceEqual<DbTableColumnMetadata>(tableInfo.Value);

残念ながら、私の場合、AAとBAの関係、およびABとBBの関係では、列「ID」が使用されます。

したがって、両方とも最初に更新されて、プリンシパルテーブルのメタデータがAAメタデータに置き換えられ、2回目はABメタデータに置き換えられます。

結果として、両方の外部キーはABを主テーブルとして生成されます!

関数パラメーターで渡されたEdmEntityTypeをセレクターで使用して、外部キー制約アノテーションに格納されているリレーションシップエンドのエンティティタイプと比較する必要があると思います。

于 2012-06-29T15:21:37.197 に答える