DBContextを使用しており、プロパティがすべて仮想である2つのクラスがあります。デバッガーで、コンテキストを照会するとプロキシオブジェクトを取得していることがわかります。ただし、コレクションプロパティを追加しようとしてもnullのままです。プロキシは、コレクションが確実に初期化されると考えました。
私のPocoオブジェクトはそのデータコンテキストの外部で使用できるため、コンストラクターでnullのコレクションのチェックを追加し、必要に応じて作成します。
public class DanceStyle
{
public DanceStyle()
{
if (DanceEvents == null)
{
DanceEvents = new Collection<DanceEvent>();
}
}
...
public virtual ICollection<DanceEvent> DanceEvents { get; set; }
}
これはデータコンテキスト外で機能しますが、クエリを使用してオブジェクトを取得すると、テストはtrueですが、設定しようとすると、次の例外が発生します: 'コレクションは既にEntityCollectionに設定されています。
Nullであることがわかり、追加することはできませんが、プロキシが既に設定されていると言うため、コレクションに設定することもできません。したがって、使用できません。よくわかりません。
DanceEventクラスは次のとおりです。
public class DanceEvent
{
public DanceEvent()
{
if (DanceStyles == null)
{
DanceStyles = new Collection<DanceStyle>();
}
}
...
public virtual ICollection<DanceStyle> DanceStyles { get; set; }
}
上記のコードから他の値型プロパティを省略しました。コンテキストクラスには、これらのクラスの他のマッピングはありません。
ここでこの問題の解決策を見つけました: Code Firstをコレクションに追加しますか?リポジトリでCode Firstを使用するには?
コレクションと遅延読み込みオブジェクト、つまりすべてのネイティブタイプを除くすべてのプロパティから「仮想」を削除しました。
しかし、使用できないnullコレクションがあり、それを有効なコレクションに設定する方法がない状況に陥る可能性があることを、私はまだ理解していません。
また、MSDNフォーラムで Rowan Millerからのこの回答 を見つけました。
こんにちは、
すべてのプロパティを仮想化すると、EFはPOCOクラスから派生する実行時にプロキシクラスを生成します。これらのプロキシにより、EFはオブジェクトの元の値をキャプチャしてから変更をスキャンするのではなく、リアルタイムで変更を見つけることができます保存すると(これには明らかにパフォーマンスとメモリ使用量の利点がありますが、メモリに多数のエンティティをロードしない限り、違いは無視できます)。これらは「変更追跡プロキシ」と呼ばれます。ナビゲーションプロパティを仮想化すると、プロキシが生成されますが、はるかに単純で、ナビゲーションプロパティにアクセスするときに遅延読み込みを実行するロジックが含まれます。
元のコードが変更追跡プロキシを生成していたため、EFは変更を検出するためにコレクションプロパティを特別なコレクションタイプに置き換えていました。コレクションをコンストラクター内の単純なリストに戻そうとすると、例外が発生します。
パフォーマンスの問題が発生しない限り、テレンスの提案に従い、ナビゲーション以外のプロパティから「仮想」を削除します。
〜ローワン
したがって、すべてのプロパティが仮想である場合にのみ、完全な「変更追跡プロキシ」に問題があるように見えます。しかし、それを考えると、なぜ変更追跡プロキシで仮想プロパティをまだ使用できないのですか? ds2.DanceEventsはnullであり、コンストラクターで設定できないため、このコードは3行目で爆発します。
DanceStyle ds2 = ctx.DanceStyles.Where(ds => ds.DanceStyleId == 1).Single();
DanceEvent evt = CreateDanceEvent();
ds2.DanceEvents.Add(evt);
上記の修正によりコードが機能するようになりましたが、まだ混乱しています。
独自の質問への回答で正しく観察したように、コレクションプロパティから「仮想」キーワードを削除すると、Entity Frameworkが変更追跡プロキシを作成できなくなるため、問題を回避できます。ただし、変更追跡プロキシは非常に便利であり、コードの適切な場所で変更を検出するのを忘れた場合に問題を防ぐことができるため、これは多くの人々にとっての解決策ではありません。
より良い方法は、POCOクラスを変更して、コンストラクターではなくgetアクセサーでコレクションプロパティをインスタンス化することです。変更追跡プロキシを作成できるように変更されたPOCOクラスは次のとおりです。
public class DanceEvent
{
private ICollection<DanceStyle> _danceStyles;
public virtual ICollection<DanceStyle> DanceStyles
{
get { return _danceStyles ?? (_danceStyles = new Collection<DanceStyle>()); }
protected set { _danceStyles = value; }
}
}
上記のコードでは、コレクションプロパティは自動ではなく、バッキングフィールドがあります。セッターを保護したままにしておくと、(プロキシ以外の)コードがこれらのプロパティを変更するのを防ぐことができます。コンストラクターは不要になり、削除されたことがわかります。
古い質問...
Pocoクラス:
public partial class MyPOCO
{
public MyPOCO()
{
this.MyPocoSub = new HashSet<MyPocoSub>();
}
//VIRTUAL
public virtual ICollection<MyPocoSub> MyPocoSub { get; set; }
}
およびプロキシコード:
public override ICollection<MyPocoSubSet> MyPocoSubSets
{
get
{
ICollection<MyPocoSubSet> myPocoSubSets = base.MyPocoSubSets;
if (!this.ef_proxy_interceptorForMyPocoSubSets(this, myPocoSubSets))
{
return base.MyPocoSubSets;
}
return myPocoSubSets;
}
set
{
if (value != this.RelationshipManager.GetRelatedEnd("WindowsFormsApplication.Models.MyPocoSubSet_MyPOCO", "MyPocoSubSet_MyPOCO_Source"))
{
// EXCEPTION
throw new InvalidOperationException("The property 'MyPocoSubSets' on type 'MyPOCO_A78FCE6C6A890855C68B368B750864E3136B589F9023C7B1D90BF7C83FD291AC' cannot be set because the collection is already set to an EntityCollection.");
}
base.MyPocoSubSets = value;
}
}
ExtityFramework 5のプロキシクラスで発生した例外を見ることができます。これは、動作がまだ存在することを意味します。