wcf - WCF:在哪一层将POCO实体转换为DTO?

标签 wcf converter dto n-tier-architecture

这是我来自here的问题的跟进。

首先,我没有在项目中使用DDD。

我有3层的WCF服务:


服务层(仅包含操作并调用BL方法)
包含所有业务逻辑类和方法的业务逻辑层
包含DbContext(LINQ-TO-EF)和POCO实体的数据访问层


WCF服务需要返回DTO对象,而我很难确定放置“翻译器”类的最佳位置,该类将我的POCO实体转换为DTO。

我对此有2个选择:





方法A

使业务逻辑方法将实体返回到服务层,并且服务层中具有将实体转换为DTO的转换器类。

优点:


业务逻辑层执行其必需的操作-验证和CRUD操作
业务逻辑层完全不需要了解DTO


缺点:


服务层现在必须包含对“数据访问层”程序集的引用,因为它从业务逻辑层接收实体。这似乎打破了三层概念,即服务层只需要引用BL层,而BL层只需要引用DAL。
这是最严重的问题:转换器类需要从实体对象创建DTO。因为它在处理DbContext之后从BL接收实体对象,所以它无法访问未使用'Include'扩展名加载的任何内容。这意味着BL方法需要将实体返回到服务层,其中包含转换程序创建DTO所需的一切。这是一个问题,因为它要求BL知道翻译器需要什么,其次-它会从数据库中获取很多不必要的数据! (也许翻译器需要返回一个'UserDto'对象,该对象的一个​​字段是'订单总数'-为什么我要从数据库中获取所有订单只是为了在翻译器中进行'Count()'调用?






方法B

将“翻译器”类从实体对象转换为放置在“业务逻辑层”本身中的DTO。在这种机制中-BL方法已经返回了DTO。

优点:


BL方法执行BL代码,然后调用“ translate_to_dto”适当的消息以将结果转换为返回的DTO。这一切都在“ DbContext”内部完成,这意味着当调用翻译器类翻译实体时,它仍然可以访问子对象,并且不需要调用“ Include”。这意味着仅从数据库中获取创建DTO所需的数据。


缺点:


现在,“业务逻辑层”中的“翻译器”类是需要了解DTO的类,尽管仅了解它们是服务层的责任!
现在,BL中的每个方法都执行纯BL(有效性检查,CRUD操作等),此外还调用转换程序方法以返回DTO。这打破了“单一职责规则”的规定,即“ BL中的方法”只能做一件特定的事情。




谁能告诉我在哪里进行'Entity ==> DTO'转换?



[更新-添加示例]

业务逻辑层具有一个名为UserManager的管理器类,该管理器类具有如下BL方法:

public UserTasksDto     GetUserInfoWithTasks(Guid userId)
{
    if (userId == Guid.Empty)
        throw new ArgumentException("User ID cannot be empty");

    using (IMyDBEntities entities = _contextFactory.GetContext())
    {
        // Get POCO Object from DbContext
        User user = entities.Users.Find(userId);
        if (user == null)
            throw new EntityNotFoundException("User was not found in the database");

        if (user.Tasks.Count() == 0)
            throw new Exception("User does not have any tasks !");

        // Call 'Translator' static method to translate POCO to DTO
        Translator.TranslateUserToUserTasksDto(user);
    }
}


从上面可以看到,BL方法调用“翻译器”方法将POCO转换为DTO。
这是在“实体”上下文内完成的,因此翻译者仍然可以访问用户的“任务”子级。

这是“翻译器”方法的外观:

class Translator
{
    public static UserTasksDto   TranslateUserToUserTasksDto ( User userPoco )
    {
        UserTasksDto dto = new UserTasksDto
        {
            UserId        = userPoco.Id,
            Username      = userPoco.Username,
            CreationDate  = userPoco.CreationDate,

            // Accessing a related entity, this is why this 'translate' method 
            // needs to be called inside the DbContext, otherwise it will except
            // (or we load all related entities using 'Include' just for the 'Count' purpose)
            Supervisor    = userPoco.Supervisor.Username,    
            NumOfTasks    = userPoco.Tasks.Count(),
            FirstTaskDate = userPoco.Tasks.OrderBy(task => task.Date).Take(1),
        }

        return dto;
    }
}


正如您在上面看到的那样-“翻译”方法从“用户” POCO对象“构建”了“ UserTasksDto”。
这是通过将“用户”对象及其相关实体中的某些字段映射到DTO来完成的。
如果此方法不在BL方法的ObjectContext中-我将得到一个异常,说我试图访问没有上下文的实体。

我希望我的问题现在更加清楚...

最佳答案

虽然有疑问

如果您需要服务层独立于BL / DAL数据实体,那么,
我认为您必须为独立模型提供一个新的抽象层(dll程序集)。

现在,您的BL和/或DAL将不返回实体,而是返回此新程序集中的模型对象。

您的服务层将不需要引用实体的DAL,而需要引用新的Models程序集。

这与视图模型类似。

现在,服务层的工作是将模型转换为DTO(如果选择这样做的话)。

优点:将释放您对BL / DAL的依赖性

缺点:模型抽象层看起来好像是多余的



编辑:

哦,我不是要从业务层退还DTO。
我的意思是从业务逻辑中返回独立的模型,以使它们真正独立于所有层,之后与之配合的任何层都可以使用它们或将其转换为所需的任何形式。

例如,服务层可以包括DTO转换器的模型,表示层可以将其转换为视图模型,存储库可以选择将其转换为XML等。现在,每层将有自己的“模型到X”转换器,BL将仅具有单一职责,并且所有层都将独立于DAL实体。

N.B.有些层可能选择直接使用它们,我相信这是您的关注,是的,如果这样做,它们将像BL返回DTO / VM一样工作,但这不是我的意图。

希望现在能解决问题...



更新:

考虑一下您是一个不会编写BL以外的任何层的开发人员。

您是否会返回DAL的实体。没有。

您是否有关于如何使用库以及将在哪些层使用库的任何概念?没有。

您将在DAL实体之上创建一个新的抽象,并将其返回到将使用您的库的WHICHEVER层。

因此,如果开发人员X出现并使用您的库来创建ACME WCF服务,我很确定X不会将您的模型对象用作他的DTO,而是X会使用您的模型作为开始来创建DTO。

开发人员Y随之出现并使用您的库来创建ACME ASP.NET MVC 3应用程序,我很确定Y不会将您的模型对象用作他的VM(查看模型),而是Y将使用您的模型作为开始来创建VM。

关于wcf - WCF:在哪一层将POCO实体转换为DTO?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11119308/

相关文章:

sql - 转换为 mm/dd/yyyy 格式

xaml - Xamarin 窗体 : IValueConverter receive null value in Convert method

java - Spring Pageable 排序暴露了 dao 的内部命名

java - 将当前时区设置为@JsonFormat 时区值

c# - 只有绝对 URI 可以用作基地址

c# - IIS 中托管的 WCF 服务库的 log4net

java - 如何在 XStream 的 XML 中使用常量

wpf - 我应该在 MVVM 中使用 DTO 作为我的数据模型吗?

c# - WCF、接口(interface)返回类型和 KnownTypes

c# - 使用 RX 观察 WCF 服务调用