15

リポジトリ パターンを使用して、集計へのアクセスと保存を提供しています。

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

たとえば、OrderandのOrderItem関係を考えてみましょう。集約ルートはOrder、独自のOrderItemコレクションを管理します。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エンティティを添付する必要がありますか? どうすればこれを一般的に行うことができますか?

4

3 に答える 3

19

Julie Lerman は、彼女の著書Programming Entity Framework: DbContextで、集計全体を更新する方法に対処する優れた方法を提供しています。

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

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

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

そのためには、主に次の 2 つの方法があります。

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

2 番目のオプションは非常に優れており、モデル内のすべてのエンティティが実装するインターフェイスを作成します。ジュリーは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;
  }
 };
}

次にOrderOrderItemクラスを変更して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 ;-) (同じグラフ内で状態Modifiedと状態を混在させることもできます。)Deleted

ジュリーは、彼女の著書でさらに先へ進むことを提案しています。

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

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

于 2014-01-29T16:21:45.947 に答える
0

集約ルートの update メソッドがうまくできたので、次のドメイン モデルを見てください。

 public class ProductCategory : EntityBase<Guid>
    {
      public virtual string Name { get; set; }
    }

    public class Product : EntityBase<Guid>, IAggregateRoot
    {
    private readonly IList<ProductCategory> _productCategories = new List<ProductCategory>();

    public void AddProductCategory(ProductCategory productCategory)
        {
            _productCategories.Add(productCategory);
        }
    }

それは製品カテゴリを持つ単なる製品でした.aggregaterootが製品(製品カテゴリではない)であるため、ProductRepositoryを作成しましたが、サービスレイヤーで製品を作成または更新するときに製品カテゴリを追加したい:

public CreateProductResponse CreateProduct(CreateProductRequest request)
        {
            var response = new CreateProductResponse();
         try
            {
                var productModel = request.ProductViewModel.ConvertToProductModel();   
                Product product=new Product();
                product.AddProductCategory(productModel.ProductCategory);
                _productRepository.Add(productModel);
                _unitOfWork.Commit();
            }
            catch (Exception exception)
            {
                response.Success = false;
            }
            return response;
        }

ドメイン内のエンティティのドメイン メソッドを作成し、それをサービスまたはアプリケーション層で使用する方法を示したかっただけです。以下のコードを見るとわかるように、データベースの productRepository を介して ProductCategory カテゴリを追加します。

product.AddProductCategory(productModel.ProductCategory);

同じエンティティを更新するために、ProductRepository を要求し、エンティティを取得して変更を加えることができます。および集計のエンティティと値オブジェクトを個別に取得するには、クエリ サービスまたは readOnlyRepository を記述できることに注意してください。

 public class BlogTagReadOnlyRepository : ReadOnlyRepository<BlogTag, string>, IBlogTagReadOnlyRepository
    {
        public IEnumerable<BlogTag> GetAllBlogTagsQuery(string tagName)
        {
            throw new NotImplementedException();
        }
    }

それが役に立てば幸い

于 2014-02-01T20:50:49.237 に答える