使用AutoMapper从数据库加载实体? [英] Using AutoMapper to load entities from the database?

查看:122
本文介绍了使用AutoMapper从数据库加载实体?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我读过的大部分内容(例如 )表示应使用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
}

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

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

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

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

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

解决方案

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

  1. 可以创建实体-通常使用工厂.这是他们生命周期的开始.
  2. 可以通过在实体上调用方法来对实体进行突变-对其状态进行修改.这就是他们在生命周期中前进的方式.通过确保实体拥有自己的状态,并且只能通过调用其方法来修改其状态,控制实体状态的逻辑全部在实体类之内,从而使业务逻辑和可维护性更高的系统更加清晰地分离. li>

使用Automapper从Dto转换为实体意味着实体正在放弃对其状态的所有权.如果dto处于无效状态,并且直接将其映射到实体,则该实体可能会以无效状态结束-您已经失去了使实体包含数据+逻辑的价值,这是DDD实体的基础./p>

要提出关于如何处理此问题的建议,我想问-您要实现的操作是什么? 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有很多要增加的地方,但其开销通常仅在具有许多复杂业务逻辑的系统中才值得.

这与您提出的问题无关,但我也建议您研究一下客户和订单"的建模.它们都是独立的聚合吗?如果是这样,将客户建模为包含订单集合可能会导致一系列问题-当客户拥有一百万个订单时会发生什么?即使该集合是延迟加载的,您也知道在某些时候会尝试加载它,从而提高性能.这里有一些有关聚合设计的好书: http://dddcommunity.org/library/vernon_2011/建议按ID(而非参考)对参考进行建模.在您的情况下,您可能具有OrderId的集合,甚至可能是一个全新的实体来表示链接-CustomerOrderLink,该链接将具有两个属性-CustomerId和OrderId.这样,您的任何实体都将没有嵌入的集合.

Most of what I've read (e.g. from the author) indicates that AutoMapper should be used to map an an entity to a DTO. It should not load anything from the database.

But what if I have this:

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
}

I need to map from DTO to entity (i.e. from CustomerDto to Customer), but first I must use that list of foreign keys to load corresponding entities from the database. AutoMapper can do that with a custom converter.

I agree that it doesn't feel right... but what are the alternatives? Sticking that logic into a controller, service, a repository, some manager class? All that seems to be pushing the logic somewhere else, in the same tier. And if I do that, I must also perform the mapping manually!

From a DDD perspective, the DTO should not be part of the domain. So AutoMapper is also not part of the domain, because it knows about that DTO. So AutoMapper is in the same tier as the controllers, services, etc.

So does it make sense to put the DTO-to-entity logic (which includes accessing the database, and possibly throwing exceptions) into an AutoMapper mapping?

EDIT
@ChrisSimon's great answer below explains from a DDD perspective why I shouldn't do this. From a non-DDD perspective, is there a compelling reason not to use AutoMapper to load from the db?

解决方案

To start with, I'm going to summarise my understanding of Entities in DDD:

  1. Entities can be created - often using a factory. This is the start of their life-cycle.
  2. Entities can be mutated - have their state modified - by calling methods on the entity. This is how they progress through their lifecycle. By ensuring that the entity owns its own state, and can only have its state modified by calling its methods, the logic that controls the entity's state is all within the entity class, leading to cleaner separation of business logic and more maintainable systems.

Using Automapper to convert from a Dto to the entity means the entity is giving up ownership of its state. If the dto is in an invalid state and you map that directly onto the entity, the entity may end up in an invalid state - you have lost the value of making entities contain data + logic, which is the foundation of the DDD entity.

To make a suggestion as to how you should approach this, I'd ask - what is the operation you are trying to achieve? DDD encourages us not to think about CRUD operations, but to think about real business processes, and to model them on our entities. In this case it looks like you are linking Orders to the Customer entity.

In an Application Service I would have a method like:

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();
    }
}

Within the LinkToOrder method, I would have explicit logic that did things like:

  • Check that order is not null
  • Check that the customer's state permits adding the order (are they currently active? is their account closed? etc.)
  • Check that the order actually does belong to the customer (what would happen if the order referenced by orderId belonged to another customer?)
  • Ask the order (via a method on the order entity) if it is in a valid state to be added to a customer.

Only then would I add it to the Customers Order's collection.

This way, the application 'flow' and infrastructure management is contained within the application/services layer, but the true business logic is contained within the domain layer - within your entities.

If the above requirements are not relevant in your application, you may have other requirements. If not, then perhaps it is not necessary to go the route of DDD - while DDD has a lot to add, its overheads are generally only worth it in systems with lots of complex business logic.

This isn't related to the question you asked, but I'd also suggest you take a look at the modelling of Customer and Order. Are they both independent Aggregates? If so, modelling Customer as containing a collection of Order may lead to problems down the road - what happens when a customer has a million orders? Even if the collection is lazy loaded, you know at some point something will attempt to load it, and there goes your performance. There's some great reading about aggregate design here: http://dddcommunity.org/library/vernon_2011/ which recommends modelling references by Id rather than reference. In your case, you could have a collection of OrderIds, or possibly even a completely new entity to represent the link - CustomerOrderLink which would have two properties - CustomerId, and OrderId. Then none of your entities would have embedded collections.

这篇关于使用AutoMapper从数据库加载实体?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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