我应该将实体(持久)对象转换为 DTO 对象吗?

Posted

技术标签:

【中文标题】我应该将实体(持久)对象转换为 DTO 对象吗?【英文标题】:Should I transform Entity (Persistent) objects to DTO objects? 【发布时间】:2017-04-04 21:21:51 【问题描述】:

我的项目分层如下:-

DAL (Entity) --> BLL (DTO) --> ApplicationComponent (ViewModel).

将有多个应用程序组件 (ApplicationComponent) 将访问 BLL。组件包括 Windows 服务、Web 服务、Web API 和 MVC 控制器。

我正在将NHibernate Entity 对象转换为DTO 对象,同时将它们从DAL 传递给BLL。在将此状态传递给ApplicationComponent 时,BLL 再次将其转换为ViewModel

这有助于我区分关注点以及数据在每一层中的处理方式。我不赞成返回 NHibernate Entity 对象查看,原因如下:-

数据暴露给UI,我想隐藏(或仅在需要时才公开),例如密码、用户类型、权限等。 在引用/连接上,NHibernate 会在访问属性时执行额外的查询,从而使延迟加载的使用无效。 向用户(Entity)公开不必要的数据会造成错误的混淆和空白。 持久性实现泄漏到BLL/UIEntity 不是为 UI 设计的。它不能在所有情况下都为UI 提供服务。 我们使用DTO 属性上的属性来验证用户输入,这与Entity 看起来很奇怪。

我在使用这种方法时面临以下问题:-

最大和最明显的问题是具有相似成员和功能的冗余对象。 我必须在每一层中编写映射器方法来转换对象。这可以通过使用AutoMapper 或类似的东西来最小化;但它并不能完全解决问题。

问题:-

    这是不是过度分离,应该避免(至少最小化)? 如果这种方法是正确的,我看不出有任何简单的方法可以完全绕过我上面提到的两个问题。请提出建议。 如果此方法不正确,请提出更正建议。

参考资料:-

    Link1 建议将 Entity 对象转移到视图中,这在我看来不是一个好主意。 Link2 建议将 Entity 映射到我已经在做的 DTO。 Link3 没有帮助。 Link4 建议使用类似自动映射工具之类的工具,这是可以的。但还是没有彻底解决问题。 Link5 是很棒的帖子。它解释了为什么我同意这些应该分开。它没有评论如何最大限度地减少由此造成的开销。 Link6 又没用了。 Link7 是一个很好的答案,它建议使用 Entity,就像在 UI 中一样如果可能。它仍然不适用于我的大部分项目。 Linl8 是另一个很好的资源,它建议像我现在做的那样以两种方式进行映射。它仍然没有建议最小化开销的方法。

【问题讨论】:

...所以,我(团队)投入了大量时间来映射数百个对象......并创建一个域。它似乎正在工作..只需将其从服务器移动到 UI 并返回......很多人互相保证 - 重新映射到 DTO 是一种方式.. 我不明白。即使使用 automapper .. 集合/引用也将是挑战。虽然:Newtonsoft.Json(解析器、实体和数组值提供者)的覆盖很少……而 JSON 序列化/反序列化正在解决所有问题。没有 DTO,没有新对象...只是托管 JSON 化... 【参考方案1】:

nhibernate 是允许您避免拥有 DAL 实体的那些 orm 之一,避免从 BLL 到 DAL 的额外映射会更好地提高性能,但如果它对您来说并不重要,最好保留让应用层松散耦合

【讨论】:

【参考方案2】:

您是否考虑过在 DTO 和实体之间创建共享接口?您不应该将您的 ORM 与应用程序的其余部分紧密耦合。或者实际上尽可能使用它们之间的接口以外的任何东西。

理论上,您可以拥有一个单独的项目,该项目仅包含您期望传递的合同/抽象。为了最大限度地减少映射开销并使其对扩展开放,您可以确保实体按预期实现接口(省略不需要的部分),并且在您需要定制 DTO 的情况下,您可以使用接口创建具有映射的模型.

添加额外的接口项目时会产生一些开销,但从长远来看,它会让您的代码更整洁、更易于维护。

namespace Data

    public class FakeRepo : IFakeRepo
    
        public IThisIsAnEntity GetEntity()
        
            return new ThisIsAnEntity();
        
    

    public class ThisIsAnEntity : IThisIsAnEntity
    
        public string HiddenField  get; set; 
        public long Id  get; set; 
        public string SomeField  get; set; 
        public string AnotherField  get; set; 
    


namespace Data.Abstractions

    public interface IFakeRepo
    
        IThisIsAnEntity GetEntity();
    


namespace Abstractions

    public interface IThisIsAnEntity : IThisIsAnSlimmedDownEntity
    
        string SomeField  get; set; 
    

    public interface IThisIsAnSlimmedDownEntity
    
        long Id  get; set; 
        string AnotherField  get; set; 
    


namespace Services.Abstractions

    public interface ISomeBusinessLogic
    
        IThisIsAnEntity GetEntity();
        IThisIsAnSlimmedDownEntity GetSlimmedDownEntity();
    


namespace Services

    public class SomeBusinessLogic : ISomeBusinessLogic
    
        private readonly IFakeRepo _repo;

        public SomeBusinessLogic(IFakeRepo repo)
        
            _repo = repo;
        

        public IThisIsAnEntity GetEntity()
        
            return _repo.GetEntity();
        

        public IThisIsAnSlimmedDownEntity GetSlimmedDownEntity()
        
            return _repo.GetEntity();
        
    


namespace UI

    public class SomeUi
    
        private readonly ISomeBusinessLogic _service;

        public SomeUi(ISomeBusinessLogic service)
        
            _service = service;
        

        public IThisIsAnSlimmedDownEntity GetViewModel()
        
            return _service.GetSlimmedDownEntity();
        

        public IComposite GetCompositeViewModel()
        
            var dto = _service.GetSlimmedDownEntity();
            var viewModel = Mapper.Map<IThisIsAnSlimmedDownEntity, IComposite>(dto);
            viewModel.SomethingSpecial = "Something else";
            return viewModel;
        
    

    
    public class SomeViewModel : IComposite
    
        public long Id  get; set; 
        public string AnotherField  get; set; 
        public string SomethingSpecial  get; set; 
    
    


namespace UI.Abstractions

    public interface IComposite : IThisIsAnSlimmedDownEntity, ISomeExtraInfo
    

    

    public interface ISomeExtraInfo
    
        string SomethingSpecial  get; set; 
    

【讨论】:

以上是关于我应该将实体(持久)对象转换为 DTO 对象吗?的主要内容,如果未能解决你的问题,请参考以下文章

我应该将 DTO 映射到客户端和服务器端的域实体/从域实体映射吗?

DTO 应该代表嵌套实体结构,还是应该将我的路径设置为每个嵌套对象都有一个端点?

服务层应该接受来自控制器的 DTO 或自定义请求对象吗?

具有业务对象、DTO 和实体/域对象的数据转换模式

将 DTO 转换为实体,反之亦然

您可以在转换为 C# 对象的 JSON 中序列化 ByteArrayContent 吗?