这是我从这里提出的问题的后续行动。
首先,我没有在我的项目中使用DDD。
我有一个 3 层的 WCF 服务:
- 服务层(仅持有操作和调用 BL 方法)
- 包含所有业务逻辑类和方法的业务逻辑层
- 保存 DbContext (LINQ-TO-EF) 和 POCO 实体的数据访问层
WCF 服务需要返回 DTO 对象,我无法确定放置将我的 POCO 实体转换为 DTO 的“转换器”类的最佳位置。
我有两个选择:
.
.
方法一
让业务逻辑方法将实体返回到服务层,并且服务层中有一个转换器类,用于将实体转换为 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。这是在“实体”上下文中完成的,因此翻译者仍然可以访问用户的“任务”子项。
这是“翻译器”方法的样子:
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中- 我会收到一个异常,说我正在尝试访问没有上下文的实体。
我希望我现在的问题更清楚...