this doc: で説明されている「単一のナビゲーションプロパティのケース」を処理する方法を理解しようとしています
2つのモデルがあるとします。
class School
{
public ICollection<Child> Childrens {get; set;}
...
}
そして
class Child
{
public int Id {get; set;}
...
}
そのため、Child
に明示的な外部キーがなくても、規則によって作成された多対1の関係です。
したがって、問題は、Child
インスタンスがあり、School.Id
がSchool
インスタンスを取得するためにデータベースを追加で呼び出すことなくこの関係を更新する方法があることを知っているかどうかです。
したがって、問題は、
Child
インスタンスがあり、School.Id
がSchool
インスタンスを取得するためにデータベースを追加で呼び出すことなくこの関係を更新する方法があることを知っているかどうかです。
はい、可能です。偽のstubSchool
エンティティインスタンスをId
のみで作成し、Attach
をDbContext
に作成できます。 (これは、EFに存在するであることを伝えます)、同じ理由でAttach
Child
インスタンスを追加し、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();
いいえ、ありません[〜#〜] any [〜#〜] ORMを使用してORMが提供する強力な型付けを使用して行う方法はありません、w/o
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();