質問

私は、集約のアクセスと保存を提供するために、リポジトリパターンを使用しています。

問題は、エンティティの関係で構成される集計の更新です。

例えば、 OrderOrderItem関係を取る。集約ルートは、独自のOrderItemコレクションを管理するOrderです。したがって、 OrderRepositoryは集約全体を更新する責任があります( OrderItemRepositoryはありません)。

データの永続性は、Entity Framework 6を​​使用して処理されます。

リポジトリメソッドを更新します( DbContext.SaveChanges()は別の場所で発生します)。

public void Update(TDataEntity item)
{
    var entry = context.Entry<TDataEntity>(item);

    if (entry.State == EntityState.Detached)
    {
        var set = context.Set<TDataEntity>();

        TDataEntity attachedEntity = set.Local.SingleOrDefault(e => e.Id.Equals(item.Id));

        if (attachedEntity != null)
        {
            // If the identity is already attached, rather set the state values
            var attachedEntry = context.Entry(attachedEntity);
            attachedEntry.CurrentValues.SetValues(item);
        }
        else
        {
            entry.State = EntityState.Modified;
        }
    }
}

上記の例では、 Orderエンティティのみが更新され、関連付けられたOrderItemコレクションは更新されません。

すべてのOrderItemエンティOrderItemを接続する必要がありOrderItemか?私はこれを一般的にどのようにすることができますか?

受け入れられた回答

Julie Lermanは、彼女の著書Programming Entity Framework:DbContextで集計全体を更新する方法を扱う良い方法を提供します。

彼女が書いているように:

切断されたエンティティグラフがサーバー側に到着すると、サーバーはエンティティの状態を認識しません。コンテキストが各エンティティの状態を認識できるように、状態を検出する方法を提供する必要があります。

この技法はpainting the stateと呼ばれます。

主に2つの方法があります。

  • モデルの知識を使用してグラフを繰り返し、各エンティティの状態を設定します
  • 状態を追跡するための一般的なアプローチを構築する

2番目のオプションは本当にいいですし、モデルのすべてのエンティティが実装するインターフェイスを作成することにあります。 Julieは、エンティティの現在の状態を示すIObjectWithStateインターフェイスを使用します。

 public interface IObjectWithState
 {
  State State { get; set; }
 }
 public enum State
 {
  Added,
  Unchanged,
  Modified,
  Deleted
 }

あなたがしなければならない最初の事はに自動的に設定することでUnchanged 、あなたにコンストラクタを追加するイベントをフックしてDBから取得したすべてのエンティティのContextクラスを:

public YourContext()
{
 ((IObjectContextAdapter)this).ObjectContext
  .ObjectMaterialized += (sender, args) =>
 {
  var entity = args.Entity as IObjectWithState;
  if (entity != null)
  {
   entity.State = State.Unchanged;
  }
 };
}

次に、 OrderおよびOrderItemクラスを変更してIObjectWithStateインターフェースを実装し、そのApplyChangesメソッドを呼び出して、ルート・エンティティーをパラメーターとして受け入れます。

private static void ApplyChanges<TEntity>(TEntity root)
 where TEntity : class, IObjectWithState
{
 using (var context = new YourContext())
 {
  context.Set<TEntity>().Add(root);

  CheckForEntitiesWithoutStateInterface(context);

  foreach (var entry in context.ChangeTracker
  .Entries<IObjectWithState>())
  {
   IObjectWithState stateInfo = entry.Entity;
   entry.State = ConvertState(stateInfo.State);
  }
  context.SaveChanges();
 }
}

private static void CheckForEntitiesWithoutStateInterface(YourContext context)
{
 var entitiesWithoutState =
 from e in context.ChangeTracker.Entries()
 where !(e.Entity is IObjectWithState)
 select e;

 if (entitiesWithoutState.Any())
 {
  throw new NotSupportedException("All entities must implement IObjectWithState");
 }
}

最後にApplyChanges ;-)を呼び出す前に、あなたのグラフの正しい状態を設定することを忘れないでください(同じグラフ内でModifiedDeleted状態を混在Modifiedせることさえできます)

ジュリーは彼の本をさらに進めることを提案している。

変更されたプロパティが追跡される方法を細かくしたいと思うかもしれません。エンティティ全体を変更済みとしてマークするのではなく、実際に変更されたプロパティのみが変更済みとしてマークされるようにすることができます。クライアントは、変更されたエンティティにマーキングするだけでなく、変更されたプロパティを記録する責任も負います。これを行う1つの方法は、変更されたプロパティ名のリストを状態追跡インターフェースに追加することである。

しかし、私の答えはすでに長すぎるので、もっと知りたいなら彼女の本を読んでください;-)


人気のある回答

私の意見(DDD特有)の答えは次のようになります:

  1. データレイヤーのEFエンティティを切り捨てます。

  2. データレイヤーがドメインエンティティ(EFエンティティではない)のみを返すようにします。

  3. EFのレイジーローディングとIQueryable()良さ(悪夢を読むIQueryable()は忘れてください。

  4. 文書データベースの使用を検討してください。

  5. 一般的なリポジトリは使用しないでください。

私があなたがEFで尋ねることを実行するために見つけた唯一の方法は、注文の子であるデータベース内のすべての注文アイテムを最初に削除または無効にしてから、データベース内のすべての注文アイテムを追加または再アクティブ化することです新しく更新された注文。



ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ