26

イライラする、これ。データベース ファーストの Entity Framework によって生成された、関連するオブジェクトのペアを次に示します。

public partial class DevelopmentType
{
    public DevelopmentType()
    {
        this.DefaultCharges = new HashSet<DefaultCharge>();
    }

    public System.Guid RowId { get; set; }
    public string Type { get; set; }

    public virtual ICollection<DefaultCharge> DefaultCharges { get; set; }
}

public partial class DefaultCharge
{
    public System.Guid RowId { get; set; }
    public decimal ChargeableRate { get; set; }
    public Nullable<System.Guid> DevelopmentType_RowId { get; set; }

    public virtual DevelopmentType DevelopmentType { get; set; }
}

DevelopmentType を保存するために呼び出しているコードは次のとおりです。エンティティ オブジェクトと DTO を区別するため、automapper が含まれます。

    public void SaveDevelopmentType(DevelopmentType_dto dt)
    {
        Entities.DevelopmentType mappedDevType = Mapper.Map<DevelopmentType_dto, Entities.DevelopmentType>(dt);
        _Context.Entry(mappedDevType).State = System.Data.EntityState.Modified;

        _Context.DevelopmentTypes.Attach(mappedDevType);
        _Context.SaveChanges();
    }

私のユーザー インターフェイスでは、最も一般的な操作は、ユーザーが DevelopmentTypes のリストを見て、それらの DefaultCharge を更新することです。したがって、上記のコードを使用してこれをテストすると、エラーなしで実行されますが、実際には何も変わりません。

デバッガーで一時停止すると、変更された DefaultCharge が関数に渡され、それが DevelopmentType にアタッチされて保存されていることが明らかです。

それをステップスルーして、ビジュアルスタジオ内で値を手動で変更すると、更新された値が保存されます。これはさらに混乱を招きます。

SQL Server プロファイラーでデータベースを監視すると、更新コマンドが親オブジェクトに対してのみ発行され、アタッチされたオブジェクトに対して発行されないことがわかります。

期待どおりに機能する他の同様のコードが他の場所にあります。ここで何が間違っていますか?

編集:

SaveDevelopmentType を呼び出す前にこれを行うと、次のことがわかりました。

        using (TransactionScope scope = new TransactionScope())
        {
            dt.Type = "Test1";
            dt.DefaultCharges.First().ChargeableRate = 99;
            _CILRepository.SaveDevelopmentType(dt);
            scope.Complete();
        }

Type への変更は保存されますが、ChargeableRate への変更は保存されません。私はそれが大規模に役立つとは思わないが、私はそれを追加すると思った.

4

7 に答える 7

23

問題は、EF が変更された DefaultCharges を認識していないことです。

の State を に設定することによりDevelopmentTypeEntityState.ModifiedEF はオブジェクトDevelopmentTypeが変更されたことのみを認識します。ただし、これは、EF は更新のみを行い、DevelopmentTypeナビゲーション プロパティは更新しないことを意味します。

回避策 (ベスト プラクティスではありません)DefaultChargeは、現在のすべてを反復処理しDevelopmentType、エンティティの状態を に設定することEntityState.Modifiedです。

さらに、最初にエンティティをコンテキストにアタッチし、後で状態を変更することをお勧めします。

コメント後に編集

DTOを使用しているので、これらのオブジェクトを異なるレイヤーまたは異なるマシンを介して転送していると思います。

この場合、1 つのコンテキストを共有することはできないため、セルフ トラッキング エンティティを使用することをお勧めします。これらのエンティティは、現在の状態 (つまり、新規、更新済み、削除済みなど) も保持します。ネット上には、セルフ トラッキング エンティティに関する多くのチュートリアルがあります。

例: MSDN - 自己追跡エンティティの操作

于 2013-08-05T13:09:16.890 に答える
5

私が知る限り、EF は子エンティティを保存できるのは、親オブジェクトが保存しようとしているのと同じコンテキストで取得された場合のみです。つまり、あるコンテキストによって取得されたオブジェクトを別のコンテキストにアタッチすると、変更を親オブジェクトに保存できますが、子オブジェクトには保存できません。これは、NHibernate に切り替えた元になった古い検索の結果です。メモリが正しく機能する場合、EF チーム メンバーがこれを確認し、この動作を変更する計画がないことを確認したリンクを見つけることができました。残念ながら、それ以来、その検索に関連するすべてのリンクが私の PC から消去されました。

あなたのケースでオブジェクトをどのように取得しているのかわからないので、これがあなたのケースに関連しているかどうかはわかりませんが、役立つ場合に備えてそこに置いてください.

これは、切り離されたオブジェクトをコンテキストにアタッチするためのリンクです。

http://www.codeproject.com/Articles/576330/Attaching-detached-POCO-to-EF-DbContext-simple-and

于 2013-08-05T11:34:37.533 に答える
5

Context.Entry()コンテキストを変更するために、エンティティを内部的に「アタッチ」しますEntityState

呼び出すことで、背面を にAttach()変更しています。この行をコメントアウトしてみてください。EntityStateUnchanged

于 2013-08-05T09:47:31.593 に答える
2

Graphdiffライブラリは、これらすべての複雑さを処理するのに非常に役立ちました。

挿入/更新/削除するナビゲーション プロパティを設定するだけで (流れるような構文を使用して)、Graphdiff が処理します。

注:プロジェクトはもう更新されていないようですが、1年以上使用しており、非常に安定しています

于 2016-04-15T23:05:50.327 に答える
1

これはすべての場合の回避策ではありませんが、ナビゲーション プロパティ オブジェクトを更新する代わりに、オブジェクトの外部キーを更新することで、これを回避できることがわかりました。

たとえば...代わりに:

myObject.myProperty = anotherPropertyObject;

これを試して:

myObject.myPropertyID = anotherPropertyObject.ID;

(他の投稿で述べたように) EF の心でオブジェクトが変更済みとしてフラグが付けられていることを確認してから、save メソッドを呼び出します。

少なくとも私のために働いた!入れ子になったプロパティを操作する場合は問題ありませんが、おそらくコンテキストを小さなチャンクに分割し、オブジェクトを複数の部分で処理して、コンテキストの肥大化を回避できます。

幸運を!:)

于 2014-04-07T20:37:06.713 に答える