私はいくつかの本を見ました(例:programming entity framework code first Julia Lerman)
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<Address> Address { get; set; }
public virtual License License { get; set; }
}
他のいくつかの書籍またはツール(例:Entity Framework Power Tools)POCOを生成すると、クラスのナビゲーションプロパティが初期化されます。
public class User
{
public User()
{
this.Addresses = new IList<Address>();
this.License = new License();
}
public int Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
public virtual License License { get; set; }
}
編集:
public class License
{
public License()
{
this.User = new User();
}
public int Id { get; set; }
public string Key { get; set; }
public DateTime Expirtion { get; set; }
public virtual User User { get; set; }
}
コレクションとナビゲーションプロパティとしての参照には明確な違いがあります。参照はエンティティです。コレクションは、エンティティを含みます。これは、ビジネスロジックの観点から、コレクションの初期化が無意味であることを意味します。つまり、エンティティ間の関連付けを定義しません。参照の設定は行います。
したがって、埋め込みリストを初期化するかどうか、またはどのように初期化するかは、純粋に好みの問題です。
「方法」については、一部の人々は遅延初期化を好む:
private ICollection<Address> _addresses;
public virtual ICollection<Address> Addresses
{
get { return this._addresses ?? (this._addresses = new HashSet<Address>());
}
Null参照例外を防ぐため、ユニットテストとコレクションの操作を容易にしますが、不必要な初期化も防ぎます。後者は、クラスに比較的多くのコレクションがある場合に違いを生じる場合があります。欠点は、それが比較的多くの配管を要することです、特に。初期化なしの自動プロパティと比較した場合。また、C#でのnull伝播演算子の出現により、コレクションプロパティの初期化の緊急性が低下しました。
...明示的なロードが適用されない限り
唯一のことは、コレクションを初期化すると、Entity Frameworkによってコレクションがロードされたかどうかを確認するのが難しくなることです。コレクションが初期化されると、次のようなステートメントが...
var users = context.Users.ToList();
...空の、nullでないUser
コレクションを持つAddresses
オブジェクトを作成します(遅延ロードは別として)。コレクションがロードされているかどうかを確認するには、次のようなコードが必要です...
var user = users.First();
var isLoaded = context.Entry(user).Collection(c => c.Addresses).IsLoaded;
コレクションが初期化されていない場合、単純なnull
チェックが実行されます。したがって、選択的明示的読み込みがコーディング慣行の重要な部分である場合、つまり...
if (/*check collection isn't loaded*/)
context.Entry(user).Collection(c => c.Addresses).Load();
...コレクションプロパティを初期化しない方が便利な場合があります。
参照プロパティはエンティティであるため、空のオブジェクトを割り当てるのは意味のあるです。
さらに悪いことに、コンストラクタでそれらを開始した場合、EFはオブジェクトの実体化または遅延読み込みによってそれらを上書きしません。それらは、積極的にそれらを置き換えるまで、常に初期値を持ちます。さらに悪いことに、空のエンティティをデータベースに保存してしまうこともあります!
また、別の効果があります:relationship fixupは発生しません。関係修正は、EFがナビゲーションプロパティによってコンテキスト内のすべてのエンティティを接続するプロセスです。 User
とLicence
を別々にロードしても、User.License
が入力され、その逆も行われます。もちろん、License
がコンストラクターで初期化された場合を除きます。これは、1:nの関連付けにも当てはまります。 Address
がコンストラクターでUser
を初期化する場合、User.Addresses
は入力されません!
Entity Frameworkコア
Entity Frameworkコアの関係修正(執筆時点では2.1)は、コンストラクターで初期化された参照ナビゲーションプロパティの影響を受けません。つまり、ユーザーとアドレスがデータベースから個別に取得されると、ナビゲーションプロパティが設定されます。
ただし、遅延読み込みはnot初期化された参照ナビゲーションプロパティを上書きします。そのため、結論として、EFコアでも、コンストラクターで参照ナビゲーションプロパティを初期化すると問題が発生する場合があります。しないでください。とにかく意味をなさない、
すべてのプロジェクトで、「コレクションはnullであってはなりません。空または値を持っています。」
これらのエンティティの作成がサードパートコード(ORMなど)の責任であり、短時間のプロジェクトで作業している場合、最初の例があります。
2番目の例の方が優れています。
NullReferenceException
を避けるドメイン駆動設計を実践している人々は、コレクションを読み取り専用として公開し、それらのセッターを避けます。 ( NHibernateの読み取り専用リストのベストプラクティスは何ですか? )
Q1:どちらが良いですか?どうして?長所と短所
コードで追加のチェックを避けるため(たとえば、Addresses
)、null以外のコレクションを公開することをお勧めします。コードベースに含めることは良い契約です。しかし、私は単一のエンティティへのnullable参照を公開しても大丈夫です(例:License
)
Q2:2番目のアプローチでは、License
クラスにもUser
クラスへの参照がある場合、スタックオーバーフローが発生します。これは、一方向の参照が必要であることを意味します。(?)どのナビゲーションプロパティを削除するかを決定する方法は?
data mapper pattern を自分で開発したとき、双方向参照を避けようとしましたが、子から親への参照はほとんどありませんでした。
ORMを使用すると、双方向参照を簡単に作成できます。
双方向参照セットを使用して単体テストのテストエンティティを構築する必要がある場合、次の手順に従います。
parent entity
をemty children collection
でビルドします。parent entity
を参照してevey child
をchildren collection
に追加します。License
型にパラメーターなしのコンストラクターがあることに注意して、user
プロパティを必須にします。
public class License
{
public License(User user)
{
this.User = user;
}
public int Id { get; set; }
public string Key { get; set; }
public DateTime Expirtion { get; set; }
public virtual User User { get; set; }
}
POCOは遅延読み込みに依存しているため、new
リストに対して冗長です。
遅延読み込みとは、エンティティを参照するプロパティに初めてアクセスしたときに、エンティティまたはエンティティのコレクションがデータベースから自動的に読み込まれるプロセスです。 POCOエンティティタイプを使用する場合、派生プロキシタイプのインスタンスを作成し、仮想プロパティをオーバーライドしてロードフックを追加することにより、遅延ロードが実現されます。
仮想修飾子を削除する場合は、遅延読み込みをオフにします。その場合、リストが初期化されないため、コードは機能しなくなります。
遅延ロードはエンティティフレームワークでサポートされている機能であり、DbContextのコンテキスト外でクラスを作成すると、依存するコードは明らかにNullReferenceException
の影響を受けることに注意してください。
HTH
Q1:どちらが良いですか?どうして?長所と短所?
仮想プロパティがエンティティコンストラクター内で設定される場合の2番目のバリアントには、「 コンストラクターでの仮想メンバー呼び出し 」と呼ばれる明確な問題があります。
ナビゲーションプロパティの初期化を行わない最初のバリアントについては、オブジェクトの作成者/作成者に応じて2つの状況があります。
最初のバリアントは、Entity Frameworkがオブジェクトを作成すると完全に有効ですが、コードコンシューマーがオブジェクトを作成すると失敗する可能性があります。
コードコンシューマが常に有効なオブジェクトを作成することを保証するソリューションは、 static factory method を使用することです。
デフォルトのコンストラクタを保護します。 Entity Frameworkは、保護されたコンストラクターで動作します。
空のオブジェクトを作成する静的ファクトリーメソッドを追加します。 User
オブジェクト。すべてのプロパティを設定します。 Addresses
およびLicense
、作成後、完全に構築されたUser
オブジェクトを返します
このように、Entity Frameworkは保護されたデフォルトコンストラクターを使用して、一部のデータソースから取得したデータから有効なオブジェクトを作成し、コードコンシューマーは静的ファクトリメソッドを使用して有効なオブジェクトを作成します。
私はこれからの答えを使用します なぜEntity Framework Code Firstプロキシコレクションがnullで、なぜ設定できないのですか?
コンストラクターの初期化に問題がありました。これを行う唯一の理由は、テストコードを簡単にするためです。コレクションがnullにならないようにすることで、テストなどで常に初期化することができます。
他の回答は完全に質問に答えますが、この質問はまだ関連性があり、Google検索で表示されるため、何かを追加したいと思います。
Visual Studioで「データベースの最初のモデルをコード化」ウィザードを使用すると、すべてのコレクションが次のように初期化されます。
public partial class SomeEntity
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public SomeEntity()
{
OtherEntities = new HashSet<OtherEntity>();
}
public int Id { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<OtherEntity> OtherEntities { get; set; }
}
ウィザードの出力は、基本的にMicrosoftからの公式の推奨事項であるため、この5年前の質問に追加する理由を説明します。したがって、すべてのコレクションをHashSet
sとして初期化します
そして個人的には、C#6.0の自動プロパティ初期化子を利用するために上記を微調整するのはかなり滑らかだと思います。
public virtual ICollection<OtherEntity> OtherEntities { get; set; } = new HashSet<OtherEntity>();