用于更新整个聚合的通用存储库 [英] Generic repository to update an entire aggregate

本文介绍了用于更新整个聚合的通用存储库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用存储库模式来提供对我的聚合的访问和保存.

I am using the repository pattern to provide access to and saving of my aggregates.

问题在于更新由实体关系组成的聚合.

OrderOrderItem的关系为例.聚合根是 Order,它管理自己的 OrderItem 集合.因此,OrderRepository 将负责更新整个聚合(不会有 OrderItemRepository).

For example, take the Order and OrderItem relationship. The aggregate root is Order which manages its own OrderItem collection. An OrderRepository would thus be responsible for updating the whole aggregate (there would be no OrderItemRepository).

使用 Entity Framework 6 处理数据持久性.

Data persistence is handled using Entity Framework 6.

更新存储库方法(DbContext.SaveChanges() 发生在别处):

Update repository method (DbContext.SaveChanges() occurs elsewhere):

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 集合.

In my above example, only the Order entity will be updated, not its associated OrderItem collection.

我必须附加所有 OrderItem 实体吗?一般情况下我怎么能做到这一点?

Would I have to attach all the OrderItem entities? How could I do this generically?

推荐答案

Julie Lerman 在她的书中提供了一个很好的方法来处理如何更新整个聚合 编程实体框架:DbContext.

Julie Lerman gives a nice way to deal with how to update an entire aggregate in her book Programming Entity Framework: DbContext.

正如她所写:

当断开连接的实体图到达服务器端时,服务器将不知道实体的状态.您需要提供一个发现状态的方法,以便可以制作上下文了解每个实体的状态.

When a disconnected entity graph arrives on the server side, the server will not know the state of the entities. You need to provide a way for the state to be discovered so that the context can be made aware of each entity’s state.

这种技术称为绘制状态.

主要有两种方法:

  • 使用您对模型的了解遍历图表并设置每个实体的状态
  • 建立一个通用的方法来跟踪状态

第二个选项非常好,它包括创建一个模型中的每个实体都将实现的接口.Julie 使用了一个 IObjectWithState 接口来告知实体的当前状态:

The second option is really nice and consists in creating an interface that every entity in your model will implement. Julie uses an IObjectWithState interface that tells the current state of the entity:

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

您要做的第一件事是自动将状态设置为 Unchanged 对于从数据库中检索到的每个实体,方法是在 Context 类中添加一个连接一个事件:

First thing you have to do is to automatically set the state to Unchanged for every entity retrieved from the DB, by adding a constructor in your Context class that hooks up an event:

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

然后更改您的 OrderOrderItem 类以实现 IObjectWithState 接口并调用此 ApplyChanges 方法接受根实体作为参数:

Then change your Order and OrderItem classes to implement the IObjectWithState interface and call this ApplyChanges method accepting the root entity as parameter:

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已删除 同一图表中的状态.)

Last but not least, do not forget to set the right state of your graph entities before calling ApplyChanges ;-) (You could even mix Modified and Deleted states within the same graph.)

朱莉建议在她的书中走得更远:

Julie proposes to go even further in her book:

您可能会发现自己想要更精细的方式跟踪修改的属性.而不是标记整个实体修改后,您可能只需要实际具有的属性更改为标记为已修改.除了将实体标记为已修改之外,客户端还负责记录哪些属性被修改.单程为此,需要将修改后的属性名称列表添加到状态跟踪接口.

you may find yourself wanting to be more granular with the way modified properties are tracked. Rather than marking the entire entity as modified, you might want only the properties that have actually changed to be marked as modified. In addition to marking an entity as modified, the client is also responsible for recording which properties have been modified. One way to do this would be to add a list of modified property names to the state tracking interface.

但由于我的回答已经太长了,如果你阅读她的想知道更多;-)

But as my answer is already too long, go read her book if you want to know more ;-)

这篇关于用于更新整个聚合的通用存储库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆