【问题标题】:WCF : In what layer to perform translation of POCO Entity to DTO?WCF:在哪一层执行 POCO 实体到 DTO 的转换?
【发布时间】:2012-06-22 13:47:59
【问题描述】:

这是我来自here 的问题的后续。

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

我有一个 3 层的 WCF 服务:

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

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

我有两个选择:

.

.

方法A

让业务逻辑方法将实体返回给服务层,并且服务层中有一个翻译器类,用于将实体翻译为 DTO。

优点:

  • 业务逻辑层完成它必须做的事情 - 验证和 CRUD 操作
  • 业务逻辑层根本不需要了解 DTO

缺点:

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

.

.

方法B

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

优点:

  • BL 方法执行 BL 代码,然后调用“translate_to_dto”适当的消息将结果转换为返回的 DTO。这一切都在“DbContext”内部完成,这意味着当调用翻译器类翻译实体时,它仍然可以访问子对象,不需要调用“包含”。这意味着只从数据库中获取创建 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。 这是在“实体”上下文中完成的,因此翻译人员仍然可以访问用户的“任务”子项。

'Translator' 方法如下所示:

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

正如您在上面看到的 - 'Translate' 方法从 'User' POCO 对象'builds' 一个 'UserTasksDto'。 这是通过将“用户”对象中的一些字段及其相关实体映射到 DTO 来完成的。 如果此方法不在 BL 方法的 ObjectContext 内部 - 我会收到一个异常,说我正在尝试访问没有上下文的实体。

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

【问题讨论】:

    标签: wcf converter dto n-tier-architecture


    【解决方案1】:

    可能没有合适的地方,但最合乎逻辑的地方是您的业务逻辑。

    【讨论】:

      【解决方案2】:

      虽然问题,

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

      您的 BL 和/或 DAL 现在不会返回实体,而是来自这个新程序集的模型对象。

      您的服务层不需要对实体的 DAL 的引用,而是对新模型程序集的引用。

      这是与视图模型类似的模式。

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

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

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


      编辑:

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

      例如,服务层可以包括一个模型到 DTO 翻译器,一个表示层可以将它们转换为视图模型,存储库可以选择将它们转换为 XML .... 等等。现在每一层都有自己的模型到 X 转换器,BL 将只有单一职责,所有层都将独立于 DAL 实体。

      注意有些层可能会选择直接使用它们,我相信这是你关心的问题,是的,如果他们这样做,他们将表现得好像 BL 返回了 DTO/VM,但这不是我的意图。

      希望现在可以解决问题...


      更新:

      想想你是一个开发者,除了 BL 不会写任何层。

      你会返回 DAL 的实体吗?不。

      您是否知道您的库将如何使用,以及哪些层将使用它。没有。

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

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

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

      【讨论】:

      • 这些术语有点混乱。只是为了确保我做对了——“模型”层是翻译层?它获取 POCO 实体并将它们转换为 DTO 吗? OR 你是说模型层需要为每个 POCO 实体“实现”一种“影子”类?
      • 如果第二部分是您的意思 - 这如何解决现在 BL 需要将创建“模型”对象所需的所有信息传递给“建模者”的问题?这对我来说就像 DTO...
      • 第二个选项。每个模型都是 POCO 实体的影子。这就是为什么我说它在 CONS 中引入了冗余。
      • 请详细说明“BL 需要将创建“模型”对象所需的所有信息传递给“建模者”。我在您的第一个问题中没有发现这一点。
      • 你也可以考虑这个答案,可能对你有帮助stackoverflow.com/questions/494109/…
      猜你喜欢
      • 2022-09-30
      • 2012-04-10
      • 1970-01-01
      • 1970-01-01
      • 2021-05-17
      • 2014-07-20
      • 1970-01-01
      • 2019-05-01
      • 2019-09-02
      相关资源
      最近更新 更多