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

标签 c# entity-framework asp.net-web-api automapper

我读过的大部分内容(例如 from the author)表明 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
}

我需要将 从 DTO 映射到实体(即从 CustomerDtoCustomer),但首先我必须使用外键列表从数据库中加载相应的实体。 AutoMapper 可以使用 custom converter 来做到这一点.

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

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

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

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

最佳答案

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

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

使用 Automapper 将 Dto 转换为实体意味着实体正在放弃其状态的所有权。如果 dto 处于无效状态并且您将其直接映射到实体,则实体可能最终处于无效状态 - 您已经失去了使实体包含数据 + 逻辑的值(value),这是 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();
    }
}

在 LinkToOrder 方法中,我会有明确的逻辑来执行如下操作:

  • 检查订单是否为空
  • 检查客户的状态是否允许添加订单(他们目前是否活跃?他们的账户是否已关闭?等)
  • 检查订单确实属于客户(如果 orderId 引用的订单属于另一个客户会怎样?)
  • 询问订单(通过订单实体上的方法)是否处于要添加到客户的有效状态。

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

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

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

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

关于c# - 使用 AutoMapper 从数据库加载实体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39189241/

相关文章:

ASP.Net vs MVC vs WebAPI 和 UseTaskFriendlySynchronizationContext

.net - IIS 无法连接到 LocalDB

c# - 如何使用 LINQ 区分列表?

c# - 从列表框项目到具有重复项目的文本框

使用 ApplicationSettingsBase 的 C# 设置 - 漫游和通用

c# - 什么是 POCO Entity Framework

c# - 如何在网格中添加多个自定义形状的按钮?

c# - LINQ 'AsEnumerable' 和 Entity Framework

c# - 异常类型 'System.ObjectDisposedException'

asp.net-web-api - 即使定义了页面大小,Kendo Grid 也会显示所有记录