web-dev-qa-db-ja.com

Entity Framework Core:追加の呼び出しなしでIDとの関係のみを更新

this doc: で説明されている「単一のナビゲーションプロパティのケース」を処理する方法を理解しようとしています

2つのモデルがあるとします。

class School
{
   public ICollection<Child> Childrens {get; set;}
   ...
}

そして

class Child
{
    public int Id {get; set;}
    ...
}

そのため、Childに明示的な外部キーがなくても、規則によって作成された多対1の関係です。

したがって、問題は、Childインスタンスがあり、School.IdSchoolインスタンスを取得するためにデータベースを追加で呼び出すことなくこの関係を更新する方法があることを知っているかどうかです。

13
silent_coder

したがって、問題は、Childインスタンスがあり、School.IdSchoolインスタンスを取得するためにデータベースを追加で呼び出すことなくこの関係を更新する方法があることを知っているかどうかです。

はい、可能です。偽のstubSchoolエンティティインスタンスをIdのみで作成し、AttachDbContextに作成できます。 (これは、EFに存在するであることを伝えます)、同じ理由でAttachChildインスタンスを追加し、Childを親コレクションに追加し、SaveChangesを呼び出します。

Child child = ...;
var schoolId = ...;

var school = new School { Id = schoolId };
context.Attach(school);
context.Attach(child);
school.Childrens.Add(child);
context.SaveChanges();

更新:エンティティにナビゲーションやFKプロパティがない場合でも、EF Coreでは、いわゆるにアクセス/変更できるため、実際には別のよりクリーンな方法があります シャドウプロパティ

シャドウプロパティは、エンティティクラスに存在しないプロパティです。これらのプロパティの値と状態は、純粋に変更トラッカーで維持されます。

あなたが名前を知ったらすぐに。あなたの場合、設定なしで慣例的に"SchoolId"になります。

したがって、偽のSchoolエンティティインスタンスは必要ありません。Childがアタッチされていることを確認し、ChangeTracker APIを使用してシャドウプロパティを設定するだけです。

context.Attach(child);
context.Entry(child).Property("SchoolId").CurrentValue = schoolId;
context.SaveChanges();
12
Ivan Stoev

更新された質問に基づく

いいえ、ありません[〜#〜] any [〜#〜] ORMを使用してORMが提供する強力な型付けを使用して行う方法はありません、w/o

  • 双方向ナビゲーションプロパティ
  • 少なくともForeignKey/Principalプロパティ(SchoolId on Child
  • 親へのシャドウ外部キーを持つ
  • 生のクエリを実行する(これは、強い型付けのためにORMを使用するという考えに勝る)と同時にDBに依存しない

    // Bad!! Database specific dialect, no strong typing 
    ctx.Database.ExecuteSqlCommandAsync("UPDATE Childs SET schoolId = {0}", schoolId);
    

ORMを使用する場合は、問題のORMフレームワークの特定の技術的な制限を受け入れる必要があります。

ドメイン駆動設計(DDD)に従ってエンティティからすべてのdb固有のフィールドを削除する場合、エンティティとしてドメインモデルを使用するのは簡単ではありません。

DDDとORMには非常に優れた相乗効果はありません。これにはもっと優れたアプローチがありますが、異なるアーキテクチャアプローチが必要です(つまり、CQRS + ES(イベントソーシングによるコマンドクエリの責任分離)。

EventSourcingからのイベントは単純な(そして不変の)メッセージクラスであり、データベースにシリアル化されたJSONとして保存し、ドメインエンティティの状態を再構築するために再生できるので、これはDDDではるかにうまく機能します。しかし、それは別の話であり、このトピックについて本全体を書くことができます。

古い答え

上記のシナリオは、Childオブジェクトが親へのナビゲーションプロパティ/「後方参照」である場合、単一のDB操作でのみ可能です。

class School
{
   public ICollection<Child> Childrens {get; set;}
   ...
}

そして

class Child
{
    public int Id {get; set;}
    // this is required if you want do it in a single operation
    public int SchoolId { get; set; }
    // this one is optional
    public School { get; set; }
    ...
}

その後、次のようなことができます:

ctx.Childs.Add(new Child { Id = 7352, SchoolId = 5,  ... });

もちろん、最初に学校IDを知っており、それが有効であることを知っている必要があります。そうしないと、SchoolIdが無効な値である場合に操作が例外をスローするため、このアプローチはお勧めしません。

childIdのみがあり、まったく新しい子を追加しない場合でも、最初に子を取得する必要があります。

// childId = 7352
var child = ctx.Childs.FirstOrDefault(c => c.Id == childId);
// or use ctx.Childs.Find(childId); if there is a chance that 
// some other operation already loaded this child and it's tracked

// schoolId = 5 for example
child.SchoolId = schoolId;
ctx.SaveChanges();
2
Tseng