应用程序的哪一层应该包含 DTO 实现
Posted
技术标签:
【中文标题】应用程序的哪一层应该包含 DTO 实现【英文标题】:Which layer of the application should contain DTO implementation 【发布时间】:2015-04-02 15:11:22 【问题描述】:最近我听到了很多关于 DTO 及其有用性的信息,但我找不到在 ASP.NET 上下文中使用它的好例子。
假设我使用三层架构:
-
数据层(使用实体框架)
业务层(WCF 服务)
表示层(MVC 4.0 Web 应用程序)
我应该在哪里将 EF Employee 对象转换为 EmployeeDTO POCO?
假设我在数据访问层进行转换,但在 WCF 服务中会发生什么?然后是否应该将其转换为另一个 DataMember
对象,并且当它到达 UI 层(MVC Web 应用程序)时是否应该第三次将其转换为模型?如果有人能帮我解决这个问题,我将不胜感激
【问题讨论】:
我目前正在研究新的网络应用程序,偶然发现了这篇好文章codeproject.com/Articles/493389/…,只是想分享一下 【参考方案1】:在类似的情况下,我曾经将 dto 放入 Core 中,这三个人都知道。所以你有
核 | ------------ | | | DAL BL PL每一层都可以使用Core.Dto.Employee
操作。每一层还在其 API 中对外公开Core.Dto.Employee
。但在内部,每一层都可以转换/适应Core.Dto.Employee
,例如您从数据库EF.Employee
中读取,然后将其转换为Core.Dto.Employee
。变换包含在图层的边界中。
如果您有几个不同的模型来表示整个层中的相同事物,例如 PL 想要 PL.Employee
而 DAL 对 EF.Employee
进行操作,那么您最终会一团糟。
【讨论】:
这样看起来更好,你在哪里进行从 EF 对象到 DTO 的转换? 回复有点晚了。我通常会尝试将转换包含在单个项目中。通常该转换仅与该特定项目相关,例如 DTO 的 EF 对象可能在 DAL 中。在某些情况下,在多个项目中重用它是有意义的——在这种情况下,我将代码迁移到核心,以便我可以重用它而不是重复【参考方案2】:您的服务层公开了 DTO。这意味着在服务层中,您可以根据希望将数据合约暴露给外界来定义数据合约。在大多数情况下,它们是扁平实体,不一定具有与您的数据库实体相同的结构。
您的服务层负责使用业务/数据层并构建您向外界公开的 DTO。
您在业务和数据层中使用的内容取决于架构。您可以拥有一个首先使用代码映射的域模型。在这种情况下,服务层将域实体映射到数据合同 (DTO)。如果您没有域模型(贫血模型),那么您也可以直接将数据库映射到您的 DTO。
ASP.NET MVC 站点使用该服务,并将其接收到的 DTO 映射到专用视图模型,然后将其传递给特定视图。
此外,您还可以决定将查询与命令分开。这是一个很好的方法,因为您作为查询请求返回的 DTO 与您发送到服务的命令完全不同。命令仅包含执行命令所需的内容并包含您想要实现的业务意图,而查询则返回您在 UI 中所需内容的扁平化模型。
其他说明:
不要公开您的数据库实体。 不要在业务层进行转换,因为它不是业务逻辑。【讨论】:
我完全不同意最后一个要点。从域实体到 dto 的转换通常是业务逻辑。数据层应该访问、持久化和返回域对象。 bll 应该根据当前页面变量、业务规则等决定返回给 UI 的内容。您不希望数据层根据业务逻辑做出决策,它需要尽可能愚蠢和松散耦合。 不同意也没关系:) 但是,映射到我的逻辑不属于业务层,因为映射甚至可能根据调用的服务操作而有所不同,而业务逻辑是相同的;因此这个映射属于服务层。 啊,您的警告是“哪里有服务层”,并且 OP 指定了 3 层而没有单独的服务层,因此我从 BLL 包含服务层的角度工作。是的,在您描述的架构中,我同意服务层就是这个地方。对于 OP 的结构,我认为您是在提倡使用 DAL。 好的,我想我们的推理是一样的 :) 我的错误,因为这里缺少服务层。【参考方案3】:看看这里添加的https://***.com/a/6310507/1771365,因为我没有足够的声誉来添加 cmets。
就我个人而言,我会在持久层和业务层之间传递实体。当您使用 MVC 时,您可能会将视图模型传递给您的控制器。此时我会将您的视图模型映射到您的 DTO。
如果您计划在所有层之间使用 DTO,则创建一个横切项目,然后您可以参考。
【讨论】:
【参考方案4】:我特别喜欢的一种方法是在您的业务层中进行 DTO 转换。
场景:您的表示层通过 DTO 调用您的业务层。您进行一些逻辑和验证,然后将 DTO 转换为实体并将其发送到您的数据访问层。
即UI --> 总线。层(转换为实体)--> 数据层
我喜欢这种方法,因为我认为数据层不应该有任何转换逻辑,并且应该根据需要接收和处理实体。这有效的另一个原因是您现在可以在转换过程中定义特定的业务规则/验证逻辑,然后再将其发送到数据层。 这是一个旧的MSDN article,但有一些很好的细节解释了类似的方法。
【讨论】:
并使用您的方法在哪里创建 DTO?在 UI 层? 您可以通过 2 种方式进行创建,或者在业务层中公开一个用于创建 DTO 的方法:UserDTO CreateUserDTO()
,它返回一个新初始化的 DTO。另一种方式(我更喜欢)是在 UI 层中简单地实例化它:var dto = new UserDTO()
。由于您的 DTO 可用于您的 UI,因此您可以简单地实例化 DTO,将其填充到 UI 中,然后将其发送到业务层以进行保存或其他任何操作。最后,当您从数据库中填充 DTO 时,您的业务层将简单地返回一个填充的 DTO 实例:UserDTO GetUserByID(int id)
【参考方案5】:
我为此目的使用了一个名为 Shared 的项目,特别是与所有层共享对象。无论名称如何,它都应该对所有层可见。
【讨论】:
【参考方案6】:不应该有任何转换。您只需将 Poco 对象用作 DTO。
EF 与 Poco 一起工作,它们可以被 WCF 序列化。
它们应该在所有项目引用的程序集中定义。
在 ASP.NET MVC 中,您可以使用 Poco - DTO 映射到 ViewModel。
【讨论】:
不幸的是,这是一种脆弱的方法,因为您无法更改对象以满足应用程序的内部需求,因为服务的外部消费者依赖于它们当前的结构。通过通过 WCF/Web API 公开单独的 DTO,您可以使内部模型得以发展,同时确保保留外部合同。以上是关于应用程序的哪一层应该包含 DTO 实现的主要内容,如果未能解决你的问题,请参考以下文章
Entity Class 到 DTO Class 的转换应该在哪一层处理?
Spring Rest API 验证应该在 DTO 中还是在实体中?