使用 AutoMapper 从数据库加载实体?

h b*_*bob 3 c# entity-framework automapper asp.net-web-api

我读过的大部分内容(例如来自作者的内容)都表明应该使用 AutoMapper 将实体映射到 DTO。它不应该从数据库加载任何内容。

但如果我有这个怎么办:

public class Customer {
  public int Id { get; set; }
  public string Name { get; set; }
  public virtual ICollection<Order> Orders { get; set; }
}

public class CustomerDto {
  public int Id { get; set; }
  public string Name { get; set; }
  public IEnumerable<int> OrderIds { get; set; }   // here is the problem
}
Run Code Online (Sandbox Code Playgroud)

我需要从 DTO 映射到实体(即从CustomerDtoCustomer),但首先我必须使用该外键列表从数据库加载相应的实体。AutoMapper 可以使用自定义转换器来做到这一点。

我同意这感觉不对……但是还有什么选择呢?将该逻辑粘贴到控制器、服务、存储库、某些管理器类中?所有这些似乎都在将逻辑推向同一层的其他地方。如果我这样做,我还必须手动执行映射!

从 DDD 的角度来看,DTO 不应该是域的一部分。因此 AutoMapper 也不是域的一部分,因为它知道 DTO。所以 AutoMapper 与控制器、服务等处于同一层。

那么,将 DTO 到实体的逻辑(包括访问数据库,并可能抛出异常)放入 AutoMapper 映射中是否有意义?

编辑
@ChrisSimon 下面的精彩答案从 DDD 的角度解释了为什么我不应该这样做。从非 DDD 的角度来看,是否有令人信服的理由不使用 AutoMapper 从数据库加载?

Chr*_*mon 6

首先,我将总结一下我对 DDD 中实体的理解:

  1. 实体可以被创建——通常使用工厂。这是它们生命周期的开始。
  2. 可以通过调用实体上的方法来改变实体 - 修改它们的状态。这就是它们在生命周期中不断进步的方式。通过确保实体拥有自己的状态,并且只能通过调用其方法来修改其状态,控制实体状态的逻辑全部位于实体类内,从而实现业务逻辑的更清晰分离和更易于维护的系统。

使用 Automapper 从 Dto 转换为实体意味着实体放弃其状态的所有权。如果 dto 处于无效状态,而您将其直接映射到实体上,则该实体最终可能会处于无效状态 - 您已经失去了使实体包含数据 + 逻辑的价值,而这是 DDD 实体的基础。

为了就如何解决这个问题提出建议,我会问 - 您想要实现的操作是什么?DDD 鼓励我们不要考虑 CRUD 操作,而是考虑真实的业务流程,并在我们的实体上对它们进行建模。在本例中,您似乎将订单链接到客户实体。

在应用程序服务中,我会有一个类似的方法:

void LinkOrdersToCustomer(CustomerDto dto)
{
    using (var dbTxn = _txnFactory.NewTransaction())
    {
        var customer = _customerRepository.Get(dto.Id);
        foreach (var orderId in dto.OrderIds)
        {
            var order = _orderRepository.Get(orderId);
            customer.LinkToOrder(order);
        }
        dbTxn.Save();
    }
}
Run Code Online (Sandbox Code Playgroud)

在 LinkToOrder 方法中,我将具有执行以下操作的显式逻辑:

  • 检查订单不为空
  • 检查客户的状态是否允许添加订单(他们当前是否处于活动状态?他们的帐户是否已关闭?等)
  • 检查订单是否确实属于该客户(如果 orderId 引用的订单属于另一个客户会发生什么?)
  • 询问订单(通过订单实体上的方法)是否处于可添加到客户的有效状态。

只有这样我才会将其添加到客户订单的集合中。

这样,应用程序“流”和基础设施管理包含在应用程序/服务层中,但真正的业务逻辑包含在域层中 - 在您的实体中。

如果上述要求与您的申请不相关,您可能还有其他要求。如果没有,那么也许没有必要走 DDD 的路——虽然 DDD 有很多东西需要添加,但它的开销通常只有在具有大量复杂业务逻辑的系统中才值得。

这与您提出的问题无关,但我还建议您看一下客户和订单的建模。它们都是独立的聚合吗?如果是这样,将 Customer 建模为包含 Order 集合可能会导致出现问题 - 当客户拥有一百万个订单时会发生什么?即使集合是延迟加载的,您也知道在某些时候会尝试加载它,并且您的性能会受到影响。这里有一些关于聚合设计的好读物:http://dddcommunity.org/library/vernon_2011/,它建议通过 Id 而不是参考来建模参考。在您的情况下,您可能有一个 OrderId 集合,甚至可能有一个全新的实体来表示链接 -CustomerOrderLink它有两个属性 - CustomerId 和 OrderId。那么您的任何实体都不会嵌入集合。