使用AutoMapper从数据库加载实体
本文关键字:加载 实体 数据库 AutoMapper 使用 | 更新日期: 2023-09-27 18:04:27
我读到的大部分内容(例如来自作者)表明,应该使用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映射到实体(即从CustomerDto
到Customer
),但首先我必须使用外键列表从数据库加载相应的实体。AutoMapper可以通过自定义转换器来实现。
我同意这感觉不对…但还有什么其他选择呢?将逻辑插入控制器、服务、存储库或某些管理器类中?所有这些似乎都将逻辑推到了同一层的其他地方。如果我这样做,我还必须手动执行映射!
从DDD的角度来看,DTO不应该是域的一部分。所以AutoMapper也不是域的一部分,因为它知道DTO。因此,AutoMapper与控制器、服务等处于同一层。
那么,将dto到实体的逻辑(包括访问数据库,并可能抛出异常)放入AutoMapper映射中有意义吗?
编辑
@ChrisSimon的精彩回答从DDD的角度解释了为什么我不应该这样做。从非ddd的角度来看,是否有令人信服的理由不使用AutoMapper从数据库加载?
首先,我将总结一下我对DDD中的实体的理解:
- 可以创建实体——通常使用工厂。这是它们生命周期的开始。
- 实体可以通过调用实体上的方法来改变它们的状态。这就是它们的生命周期。通过确保实体拥有自己的状态,并且只能通过调用其方法来修改其状态,控制实体状态的逻辑都在实体类中,从而导致更清晰的业务逻辑分离和更可维护的系统。
使用Automapper从Dto转换为实体意味着实体放弃了对其状态的所有权。如果dto处于无效状态,而您将其直接映射到实体,则实体可能最终处于无效状态—您已经失去了使实体包含数据+逻辑的价值,这是DDD实体的基础。
为了给你一个建议,你应该如何处理这个问题,我想问——你想要实现的操作是什么?DDD鼓励我们不要考虑CRUD操作,而是考虑实际的业务流程,并在我们的实体上对它们建模。在本例中,看起来您正在将Orders链接到Customer实体。
在应用程序服务中,我将有这样一个方法:
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引用的订单属于另一个客户会发生什么?)
- 询问订单(通过订单实体上的方法)是否处于添加给客户的有效状态。
只有这样,我才会将它添加到Customers Order的集合中。
这样,应用程序"流"和基础设施管理包含在应用程序/服务层中,但真正的业务逻辑包含在域层中——在您的实体中。
如果上述要求与你的申请无关,你可能有其他要求。如果没有,那么也许没有必要走DDD的路线—虽然DDD有很多要添加的内容,但它的开销通常只在具有许多复杂业务逻辑的系统中值得使用。
这与你问的问题无关,但我也建议你看一下客户和订单的建模。它们都是独立的聚合体吗?如果是这样,将Customer建模为包含订单集合可能会导致问题——当客户有一百万个订单时会发生什么?即使集合是惰性加载的,您也知道在某些时候会有东西试图加载它,这会影响您的性能。这里有一些关于聚合设计的优秀读物:http://dddcommunity.org/library/vernon_2011/,它推荐我的建模参考,而不是参考。在您的例子中,您可以有一个OrderId的集合,或者甚至可能有一个全新的实体来表示链接——CustomerOrderLink
,它将有两个属性——CustomerId和OrderId。