【发布时间】: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