なぜ私のエンティティフレームワークコードは最初にプロキシコレクションがヌルで、なぜ設定できないのですか?
-
28-09-2019 - |
質問
私はDBContextを使用しており、プロパティがすべて仮想的な2つのクラスがあります。デバッガーでは、コンテキストを照会するときにプロキシオブジェクトを取得していることがわかります。ただし、コレクションプロパティは、私がそれに追加しようとすると、まだnullです。プロキシは、コレクションが初期化されることを保証すると思いました。
私のpocoオブジェクトはデータコンテキストの外で使用できるため、コンストラクターにnullであるコレクションのチェックを追加し、必要に応じて作成します。
public class DanceStyle
{
public DanceStyle()
{
if (DanceEvents == null)
{
DanceEvents = new Collection<DanceEvent>();
}
}
...
public virtual ICollection<DanceEvent> DanceEvents { get; set; }
}
That works outside the data context but if I retrieve an object using a query, although the test is true, when I try to set it, I get following exception: 'The property 'DanceEvents' on type 'DanceStyle_B6089AE40D178593955F1328A70EAA3D8F0F01DDE9F9FBD615F60A34F9178B94' cannot be set because theコレクションはすでにエンティティコレクションに設定されています。
私はそれがnullであることがわかります、そして私はそれに追加することはできませんが、プロキシはすでに設定されていると言っているので、コレクションにそれを設定することもできません。したがって、使用できません。よくわかりません。
これがDanceEventクラスです:
public class DanceEvent
{
public DanceEvent()
{
if (DanceStyles == null)
{
DanceStyles = new Collection<DanceStyle>();
}
}
...
public virtual ICollection<DanceStyle> DanceStyles { get; set; }
}
上記のコードから他の値タイプのプロパティを省略しました。コンテキストクラスのクラスの他のマッピングはありません。
解決 2
ここでこの問題の解決策を見つけました: コード最初にコレクションに追加しますか?最初にリポジトリでコードを使用する方法は?
コレクションと怠zyなロードされたオブジェクト、つまりすべてのネイティブタイプを除くすべてのプロパティから「仮想」を削除しました。
しかし、私はあなたが使用できないヌルコレクションがあり、それを有効なコレクションに設定する方法がない状況に終わる方法をまだ理解していません。
私も見つけました ローワン・ミラーからのこの答え MSDNフォーラムで
やあ、
すべてのプロパティを仮想的に作成すると、EFはPOCO分類から派生する実行時にプロキシクラスを生成します。これらのプロキシにより、EFはオブジェクトの元の値をキャプチャしてから変更をスキャンするのではなく、リアルタイムの変更について調べることができます。保存すると(これは明らかにパフォーマンスとメモリの使用の利点がありますが、多くのエンティティがメモリにロードされない限り、差は無視できます)。これらは「変更追跡プロキシ」と呼ばれます。ナビゲーションプロパティを仮想にすると、プロキシが生成されますが、ナビゲーションプロパティにアクセスするときに怠zyなロードを実行するためのロジックがはるかにシンプルです。
元のコードがChange Tracking Proxiesを生成していたため、EFはCollectionプロパティを特別なコレクションタイプに置き換えて、変更について調べるのに役立ちました。コンストラクターのシンプルなリストにコレクションを設定しようとするので、例外を取得します。
パフォーマンスの問題が発見されていない限り、私はTerrenceの提案に従い、非視聴プロパティから「仮想」を削除するだけです。
〜ローワン
したがって、すべてのプロパティが仮想である場合、完全な「変更追跡プロキシ」だけで問題があるようです。しかし、それを考えると、なぜ変更追跡プロキシで仮想プロパティを使用できないのでしょうか? DS2.DanceEventsがNULLであり、コンストラクターで設定できないため、このコードは3行目に爆発します。
DanceStyle ds2 = ctx.DanceStyles.Where(ds => ds.DanceStyleId == 1).Single();
DanceEvent evt = CreateDanceEvent();
ds2.DanceEvents.Add(evt);
上記の修正のために私のコードが機能しているにもかかわらず、私はまだ混乱しています。
他のヒント
自分の質問に対する答えで正しく観察したように、エンティティフレームワークが変更追跡プロキシを作成するのを防ぐことにより、コレクションプロパティから「仮想」キーワードを削除することで、問題の周りに機能します。ただし、これは多くの人々にとって解決策ではありません。なぜなら、追跡プロキシは非常に便利であり、コードの適切な場所での変更を検出するのを忘れたときに問題を防ぐのに役立つからです。
より良いアプローチは、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のプロキシクラスで提起されました。これは、動作がまだ存在することを意味します。