42

私は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ですが、設定しようとすると、次の例外が発生します。'DanceStyle_B6089AE40D178593955F1328A70EAA3D8F0F01DDE9F9FBD615F60A34F9178B94'のプロパティ'コレクションはすでにEntityCollectionに設定されています。

nullで追加できないことはわかりますが、プロキシがすでに設定されていると言っているため、コレクションに設定することもできません。そのため使用できません。よくわかりません。

DanceEventクラスは次のとおりです。

public class DanceEvent
{
    public DanceEvent()
    {
        if (DanceStyles == null)
        {
            DanceStyles = new Collection<DanceStyle>();
        }
    }
    ...
    public virtual ICollection<DanceStyle> DanceStyles { get; set; }
}

上記のコードから他の値型プロパティを省略しました。コンテキストクラス内のこれらのクラスに対する他のマッピングはありません。

4

3 に答える 3

49

自分の質問への回答で正しく観察したように、コレクションプロパティから「virtual」キーワードを削除すると、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; }
    }
}

上記のコードでは、コレクションプロパティは自動ではなく、バッキングフィールドがあります。セッターを保護したままにして、コード(プロキシ以外)がこれらのプロパティを後で変更しないようにすることをお勧めします。コンストラクターが不要になり、削除されたことがわかります。

于 2012-03-28T17:28:30.743 に答える
13

私はここでこの問題の解決策を見つけました:コードを最初にコレクションに追加しますか?リポジトリでCodeFirstを使用する方法は?

コレクションと遅延ロードされたオブジェクト、つまりすべてのネイティブタイプを除くすべてのプロパティから「virtual」を削除しました。

しかし、使用できないnullコレクションがあり、それを有効なコレクションに設定する方法がないという状況に陥る方法がまだわかりません。

また、MSDNフォーラムでRowanMillerからこの回答を見つけました。

やあ、

すべてのプロパティを仮想化すると、EFは実行時にPOCOクラスから派生するプロキシクラスを生成します。これらのプロキシにより、EFはオブジェクトの元の値をキャプチャして変更をスキャンするのではなく、リアルタイムで変更を検出できます。保存するとき(これには明らかにパフォーマンスとメモリ使用量の利点がありますが、メモリに多数のエンティティがロードされていない限り、違いはごくわずかです)。これらは「変更追跡プロキシ」と呼ばれ、ナビゲーションプロパティを仮想化すると、プロキシは引き続き生成されますが、はるかに単純で、ナビゲーションプロパティにアクセスするときに遅延読み込みを実行するロジックが含まれています。

元のコードは変更追跡プロキシを生成していたため、EFはコレクションプロパティを特別なコレクションタイプに置き換えて、変更を検出できるようにしました。コレクションをコンストラクターの単純なリストに戻そうとすると、例外が発生します。

パフォーマンスの問題が発生しない限り、Terrenceの提案に従い、ナビゲーション以外のプロパティから「仮想」を削除します。

〜ローワン

したがって、すべてのプロパティが仮想である場合にのみ、完全な「変更追跡プロキシ」で問題が発生するようです。しかし、それでも、変更追跡プロキシで仮想プロパティを使用できないのはなぜですか?ds2.DanceEventsがnullであり、コンストラクターで設定できないため、このコードは3行目で爆発します。

DanceStyle ds2 = ctx.DanceStyles.Where(ds => ds.DanceStyleId == 1).Single();
DanceEvent evt = CreateDanceEvent();
ds2.DanceEvents.Add(evt);

上記の修正によりコードが機能するようになりましたが、まだ混乱しています。

于 2010-11-01T14:25:26.150 に答える
3

古い質問...

ポコクラス:

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のプロキシクラスで例外が発生しました。これは、動作がまだ存在していることを意味します。

于 2013-08-06T18:44:41.903 に答える