2017-10-23 18 views
1

私は複雑なオブジェクト階層をエンタープライズアプリケーションに持っています。私はそれを単純で抽象的にしておきたいと思いますが、私が扱っていることをまだ代表しています。抽象親への参照プロパティのマッピング

私のプロジェクトは、同じ種類のオブジェクトのいくつかのスタイルを扱います。このために、エンティティオブジェクトのTPT構造を実装しました:

public abstract class BaseWidget { 
    public int Id { get; set; } 
    // etc... 
} 

// About a dozen concrete implementations already exist and work great! 
public class ExistingWidget : BaseWidget { 
    // Other properties 
} 

私は今行っている新しいタイプがあります。オブジェクトには共通のプロパティがありますが、サブタイプに応じて必要な詳細がいくつかあります。このため、TPHはそのタイプのプロパティがすべてのサブタイプで同じであるため、TPHを設定します。唯一の違いは、どの詳細オブジェクトが必要なのかです。私はこの本のように私のDbContextにマッピングされている

public abstract NewWidgetBase : BaseWidget { 
    public int EmployeeNumber { get; set; } 
    public DateTime EffectiveDate { get; set; } 
} 

public NewWidgetA : NewWidgetBase { 
} 

public NewWidgetB : NewWidgetBase { 
} 

:この時点で

protected override void OnModelCreating(DbModelBuilder modelBuilder) { 
    modelBuilder.Entity<NewWidgetBase>() 
       .Map<NewWidgetA>(w => w.Requires("Discriminator").HasValue("a")) 
       .Map<NewWidgetB>(w => w.Requires("Discriminator).HasValue("b")); 

は、私は統合テストを使用に成功し、私は両方のテーブルに保存することができますことを確認しています。

は今、私は細部に追加する:

public class FooDetails { 
    public int Id { get; set; } 
    public int NewWidgetId { get; set; } 
    // ... 
    [ForeignKey(nameof(NewWidgetId))] 
    public NewWidgetBase NewWidget { get; set; } 
} 

public class BarDetails { 
    public int Id { get; set; } 
    public int NewWidgetId { get; set; } 
    // ... 
    [ForeignKey(nameof(NewWidgetId))] 
    public NewWidgetBase NewWidget { get; set; } 
} 

私はその後、私の適切な​​オブジェクトにそれらの参照プロパティを追加します。

public class NewWidgetA { 
    // ... 
    public FooDetails Foo { get; set; } 
} 

public class NewWidgetB { 
    // ... 
    public FooDetails Foo { get; set; } 
    public BarDetails Bar { get; set; } 
} 

私は、これを実行する典型的なマッピングがうまくいくと仮定し、次のエラーましてみました:それと

System.Data.Entity.Infrastructure.DbUpdateException: An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details. ---> System.Data.Entity.Core.UpdateException: Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.

を、私はそれが正しい関係方向を持っていないことを理解し、キーがマップされます。だから私は、明示的に再びDbContext以内にそれを設定するために行ってきました:

modelBuilder.Entity<NewWidgetA>() 
      .HasRequired(w => w.Foo) 
      .WithRequiredDependent(); 

しかし、それは私にエラーを与える:

System.InvalidOperationException: A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'WidgetId'.

私は「some other」「questions」を見ていない、とそれらの答えのどれも助けて頂きました。

最後の溝の努力として、私はFuncを取る.WithRequiredDependent()のためのオーバーロードを使ってみました。しかし、私は抽象基盤としてプロパティを持っているので、私はマッピングしているのとまったく同じ型ではないので、それは文句を言う。したがって、私はそうのようにそれをキャストしてみてください。

modelBuilder.Entity<NewWidgetA>() 
      .HasRequired(w => w.Foo) 
      .WithRequiredDependent(f => (NewWidgetA)f.Widget); 

modelBuilder.Entity<NewWidgetB>() 
      .HasRequired(w => w.Foo) 
      .WithRequiredDependent(f => (NewWidgetB).Widget); 
modelBuilder.Entity<NewWidgetB>() 
      .HasRequired(w => w.Bar) 
      .WithRequiredDependent(b => (NewWidgetB).Widget); 

しかし、これもエラーを与える:

The ForeignKeyAttribute on property 'Widget' on type '...Foo' is not valid. The foreign key name 'WidgetId' was not found on the dependent type 'NewWidgetA'. The Name value should be a comma separated list of foreign key property names.

これは私が私がやりたいことができないんだと信じて私をリードしています抽象的な特性を有する。私は行方不明のこの関係をマップする方法はありますか? 1〜2ヵ月以内にタイプが増え、プロパティのリストが扱いにくくなるということを知っているので、それぞれに固有の参照プロパティを持たせたくありません。

答えて

1

ことは可能だが、唯一と一方向一対一WidgetDetails依存あるShared Primary Key Association、(ナビゲーションプロパティのみWidget側で)。

スタートDetailsエンティティからナビゲーションとFKのプロパティを削除することによって:

public class FooDetails { 
    public int Id { get; set; } 
    // ... 
} 

public class BarDetails { 
    public int Id { get; set; } 
    // ... 
} 

と、次の流暢な設定を使用します。

modelBuilder.Entity<NewWidgetA>() 
    .HasRequired(w => w.Foo) 
    .WithRequiredPrincipal(); 

modelBuilder.Entity<NewWidgetB>() 
    .HasRequired(w => w.Foo) 
    .WithRequiredPrincipal(); 

modelBuilder.Entity<NewWidgetB>() 
    .HasRequired(w => w.Bar) 
    .WithRequiredPrincipal(); 

WithRequiredPrincipal()コールに注意してください。 (1)Widgetがプリンシパルであり、(2)DetailsからWidgetまでのナビゲーションプロパティがないことをEFに伝えている。

CreateTable(
    "dbo.BaseWidget", 
    c => new 
     { 
      Id = c.Int(nullable: false, identity: true), 
     }) 
    .PrimaryKey(t => t.Id); 

CreateTable(
    "dbo.ExistingWidget", 
    c => new 
     { 
      Id = c.Int(nullable: false), 
     }) 
    .PrimaryKey(t => t.Id) 
    .ForeignKey("dbo.BaseWidget", t => t.Id) 
    .Index(t => t.Id); 

CreateTable(
    "dbo.NewWidgetBase", 
    c => new 
     { 
      Id = c.Int(nullable: false), 
      EmployeeNumber = c.Int(nullable: false), 
      EffectiveDate = c.DateTime(nullable: false), 
      Discriminator = c.String(nullable: false, maxLength: 128), 
     }) 
    .PrimaryKey(t => t.Id) 
    .ForeignKey("dbo.BaseWidget", t => t.Id) 
    .Index(t => t.Id); 

CreateTable(
    "dbo.FooDetails", 
    c => new 
     { 
      Id = c.Int(nullable: false), 
      Data = c.String(), 
     }) 
    .PrimaryKey(t => t.Id) 
    .ForeignKey("dbo.NewWidgetBase", t => t.Id) 
    .Index(t => t.Id); 

CreateTable(
    "dbo.BarDetails", 
    c => new 
     { 
      Id = c.Int(nullable: false), 
      Data = c.String(), 
     }) 
    .PrimaryKey(t => t.Id) 
    .ForeignKey("dbo.NewWidgetBase", t => t.Id) 
    .Index(t => t.Id); 
+0

恐ろしい:

結果のデータベーススキーマは次のようなものです!それは理にかなっていますが、少し直感的です。私は一日働き始めるとそれを撃つだろう。 – krillgar

関連する問題