1

問題の概要:私はマスターと詳細エンティティを持っています。マスター(myMaster)を初期化すると、Details(myMaster.Detail)のインスタンスが作成され、myMasterが追加されると、その両方がデータベース内に残っているように見えます。しかし、コンテキストをリロードしてmyMasterReloaded.detailにアクセスすると、そのプロパティは初期化されません。しかし、コンテキストから直接詳細を引き出すと、これは魔法のようにmyMasterReloaded.detailを初期化するようです。私はこれを以下の最小ユニットテストの例で蒸留しました。これは「機能」ですか、それとも重要な概念の詳細が欠けていますか?問題エンティティフレームワークでの保存(概念的ヘルプが必要)

//DECLARE CLASSES 
public class Master 
{ 
    [Key, DatabaseGenerated(DatabaseGenerationOption.Identity)] 
    public Guid MasterId { get; set; } 
    public Detail Detail { get; set; } 
    public Master() { Detail = new Detail(); } 
} 

public class Detail 
{ 
    [Key, DatabaseGenerated(DatabaseGenerationOption.Identity)] 
    public Guid DetailId { get; set; } 
    public Master MyMaster{ get; set; } 
} 

public class MyDbContext : DbContext 
{ 
    public DbSet<Master> Masters { get; set; } 
    public DbSet<Detail> Details { get; set; } 
    protected override void OnModelCreating(ModelBuilder modelBuilder) 
    { 
     modelBuilder.Entity<Master>() 
      .HasOptional(x => x.Detail) 
      .WithOptionalPrincipal(x => x.MyMaster) 
      .WillCascadeOnDelete(true); 
    } 
} 

//PERFORM UNIT TEST 
[TestMethod] 
public void UnitTestMethod() 
{ 
    //Start with fresh DB 
    var context = new MyDbContext(); 
    context.Database.Delete(); 
    context.Database.CreateIfNotExists(); 

    //Create and save entities 
    var master = context.Masters.Create();    
    context.Masters.Add(master); 
    context.SaveChanges(); 

    //Reload entity 
    var contextReloaded = new MyDbContext(); 
    var masterReloaded = contextReloaded.Masters.First(); 

    //This should NOT Pass but it does.. 
    Assert.AreNotEqual(master.Detail.DetailId, masterReloaded.Detail.DetailId); 

    //Let's say 'hi' to the instance of details in the db without using it. 
    contextReloaded.Details.First(); 

    //By simply referencing the instance above, THIS now passes, contracting the first Assert....WTF?? 
    Assert.AreEqual(master.Detail.DetailId, masterReloaded.Detail.DetailId); 
} 

(これは、より洗練されたエンティティセットのための争点である。私は単に私が単に複合型で詳細を置き換えることはできません最も単純な場合にこれをダウン蒸留しました)。

乾杯、 ロブ

答えて

0

マット・ハミルトン(上記参照)正しかったです。問題だった:

  1. 詳細プロパティは、裏当て部材を経由して、コンストラクタ内で、またゲッター/セッターを通じてをインスタンス化することはない必要があります。新しいインスタンスでプロパティを含むエンティティをインスタンス化すると便利な場合は、データベースからオブジェクトを再構築するときにEntity Frameworkによって自動的に実行されない個別の初期化メソッドを持つと便利です。
  2. Detailプロパティを正しく動作させるには、Masterクラスの仮想と宣言する必要があります。

次WILL PASS(予想通り/希望)

public class Master 
{ 
    [Key, DatabaseGenerated(DatabaseGenerationOption.Identity)] 
    public Guid MasterId { get; set; } 

    //Key new step: Detail MUST be declared VIRTUAL 
    public virtual Detail Detail { get; set; } 
} 

public class Detail 
{ 
    [Key, DatabaseGenerated(DatabaseGenerationOption.Identity)] 
    public Guid DetailId { get; set; } 
    //Set this to be VIRTUAL as well 
    public virtual Master MyMaster { get; set; } 
} 

public class MyDbContext : DbContext 
{ 
    public DbSet<Master> Masters { get; set; } 
    public DbSet<Detail> Details { get; set; } 
    protected override void OnModelCreating(ModelBuilder modelBuilder) 
    { 
     //This sets up a BI-DIRECTIONAL relationship 
     modelBuilder.Entity<Master>() 
      .HasOptional(x => x.Detail) 
      .WithOptionalPrincipal(x => x.MyMaster) 
      .WillCascadeOnDelete(true); 
    } 
} 


[TestMethod] 
public void UnitTestMethod() 
{ 

    var context = new MyDbContext(); 
    context.Database.Delete(); 
    context.Database.CreateIfNotExists(); 

    //Create and save entities 
    var master = context.Masters.Create(); 

    //Key new step: Detail must be instantiated and set OUTSIDE of the constructor 
    master.Detail = new Detail(); 
    context.Masters.Add(master); 
    context.SaveChanges(); 

    //Reload entity 
    var contextReloaded = new MyDbContext(); 
    var masterReloaded = contextReloaded.Masters.First(); 

    //This NOW Passes, as it should 
    Assert.AreEqual(master.Detail.DetailId, masterReloaded.Detail.DetailId); 

    //This line is NO LONGER necessary 
    contextReloaded.Details.First(); 

    //This shows that there is no change from accessing the Detail from the context 
    Assert.AreEqual(master.Detail.DetailId, masterReloaded.Detail.DetailId); 
    } 

最後に、双方向の関係を持つことも必要ではありません。 「MyMaster」への言及は、安全明細クラスから除去することができ、以下のマッピングを代わりに使用することができる:上記で

modelBuilder.Entity<Master>() 
      .HasOptional(x => x.Detail) 
      .WithMany() 
      .IsIndependent(); 

を、context.Details.Remove(master.Detail)を行う、マスターをもたらしました。詳細== nullがtrueである(期待通り/希望)。

私はインスタンス化していないため、コンストラクタ内のエンティティの仮想リストを初期化できる(たとえば、myDetails = new List();)を呼び出すことができるX-to-Manyマッピングからいくつかの混乱が浮かび上がったと思います実体そのもの。

ところで、ケースには誰もが、次は私のために働いたマスターから詳細のLISTに1対多の単方向マップ、といくつかの困難を持っている:

modelBuilder.Entity<Master>() 
      .HasMany(x => x.Details) 
      .WithMany() 
      .Map((x) => 
         { 
          x.MapLeftKey(m => m.MasterId, "MasterId"); 
          x.MapRightKey(d => d.DetailId, "DetailId"); 
         }); 

乾杯、ロブ

2

は、私はあなたが最初マスターをリロードするとき、あなたは熱心な仕掛け詳細持っていないので、詳細エンティティがエンティティになりませんので、それはだ思いフレームワーク "グラフ"(内部メモリ)。グラフの唯一のものは、マスターエンティティです。あなたはクエリ詳細(「さんが挨拶しましょう」)、それをグラフにロードされ、基準がFK関連付けに基づいて解決されたときにマスターがあるよう

はしかし、したがって、最終的なテストは合格します現在詳細に関連しています。

私は間違っている可能性がありますが、それはそのようなものです。

遅延ロードが有効になっていますか?そうでない場合は、必要な関係を熱心にロードする必要があります。これに代えて

var masterReloaded = contextReloaded.Masters.First(); 

はこれを試してみてください:

var masterReloaded = contextReloaded.Masters.Include(x => x.Detail).First(); 
+0

それと、新しいMasterが自動的に新しいDetailを(そのctorで)作成するので、データベースからMasterをロードすると、新しいDetailインスタンスが作成されます。 –

+0

エンティティをインスタンス化するときにEF4が何らかの理由でデフォルトのctorをバイパスしない限り。何でも可能だと思います。 –

+0

ctor内にエンティティを作成しないでください。 EFは、それがするように設計されたことをしましょう。 – RPM1984

関連する問題