【问题标题】:Where should the business logic be in this pattern?在这个模式中业务逻辑应该在哪里?
【发布时间】:2012-10-06 18:41:05
【问题描述】:

这是我在阅读了很多关于 DDD、TDD 和 Repository / UnitOfWork 模式之后的第一次尝试,以制作我自己的应用程序。

我在 .NET 4.0 上使用 Entity Framework、MVC 4(将运行此应用程序的服务器是 Windows 2003)

这是基本的简化模式逻辑(最初使用 IRepository、IUnitOfWork、GenericRepository 并使用 IEntity 接口扩展 EF POCO 以提供对公共 ID 字段的访问权限。但是这个简化的示例足以问我的问题)

View -> ViewModel -> Controller <- UnitOfWork <- Repository <- EntityFramework <- Database

查看

Model.Employee.GetSeniority()

EmployeeDetailsViewModel

Employee e { get; set; }

员工

DateTime dateHired { get; set; }
TimeSpan GetSeniority()
{
    return DateTime.Today - dateHired;
}

控制器 EmployeeDetails()

using(var unitOfWork = new UnitOfWork) {
    return View(EmployeeDetailsViewModel model = new EmployeeDetailsViewModel {
        e = unitOfWork.GetEmployeRepository().Find(o=>o.id == id)
    });
}

UnitOfWork GetEmployeRepository()

return (_employeeRepository ?? _employeeRepository = new EmployeeRepository(this.dbContext));

存储库查找()

dbContext.Configuration.EnableProxyCreation = false;
Employee e = dbContext.Employees.Where(expression);
dbContext.Configuration.EnableProxyCreation = true;
return e;

实际上一切正常。问题是我觉得这里有些地方很不对劲,我不确定应该在哪一层修复。

在很多人(Hi Darin)建议总是将 ViewModels 传递给视图而不是模型之后,我开始这样做。但是,我正在做的(我认为)并没有好多少。我只是将我的模型封装在视图模型中。起初,这听起来并没有那么糟糕,因为我的 Find() 方法会在获取对象之前关闭代理,这将导致 POCO 不了解持久性。但是,现在想在 POCO 中添加一些逻辑,感觉有些不对劲。

我认为问题在于我的业务逻辑在哪里,以及我的 Employee POCO 应该映射到 DTO 对象这一事实。但是,我应该在哪里将 Employee POCO 转移到 EmployeeDTO?那应该是存储库、控制器还是其他什么的任务?我也不确定我应该把我的业务逻辑放在哪里(就像示例中显示的 GetSeniority() 一样简单)。应该通过部分类将其添加到 EF POCO 中还是应该在 DTO 中?或者 Employee -> EmployeeDTO 转移中是否还有另一个缺失的步骤?

【问题讨论】:

    标签: asp.net-mvc entity-framework design-patterns domain-driven-design repository-pattern


    【解决方案1】:

    这是一个很好的问题。看起来您正在尝试找到非常棒的干净分离。我会解决这个问题。你有数据访问,你有 UI 显示,在两者之间你有你的业务逻辑。如果您想使用域模型方法,我将按照以下方式构建它。

    • 切勿在存储库之外公开 EntityFramework 实体类。您可以选择从存储库返回 Dto (POCO') 或域对象。如果您希望 Dto 进行更多分离,那很好,您只需要另一个层(例如服务层)将 Dto 转换为域对象。

    • 将业务逻辑放入域对象中。因此 Domain.Employee.GetSenority() 将在您的域对象上。

    • 任何不适合您的域对象的逻辑都可以驻留在您的 UnitOfWork 或服务层中。

    • 在控制器中将域对象转换为 ViewModel。此时将 Employee.GetSenority() 映射到 MyViewModel.Senority 属性。基本上,您的 ViewModel 是一个 Dto,仅包含通常不多的视图特定逻辑。

    • 您在哪里调用存储库。您可以使用 UnitOfWork 模式,也可以简单地创建一个服务层类。这里的关键是这些应该可用于其他应用程序类型。例如,如果您要编写桌面或 Windows 8 样式应用程序,您可能希望将其中任何一个与您的域实体一起重用。

    我相信您对此很满意。祝你好运。

    【讨论】:

    • 我的 DbContext 没有暴露在存储库之外,所以这很好。但是,您说 DTO 是 POCO,但实体框架模型也是 POCO(至少在我使用的版本中,如前所述,它们是带有代理的 POCO,在关闭代理后,它们会变成纯 POCO)。在我的场景中究竟应该考虑什么域对象?实体框架对象 (POCO)?还是 DTO? “将域对象转换为 ViewModel”到底是什么意思?如果我需要 ViewModel 中的员工列表,我是否需要一个带有 ViewModel 列表的 ViewModel?
    • (续) UnitOfWork 延迟加载(或延迟实例化?)具有唯一 DbContext 的存储库。 UnitOfWork 也是一个 IDisposible 实现,打开一次性,将保存 DbContext。这是(我相信)使用 EF 的最佳方式,因为它会在对该 UnitOfWork 实例进行所有修改后执行单个事务。让它成为一次性的只会让我的代码看起来很性感:)
    • 对我来说 Dto 和 POCO 是一样的。您可以将 Dto 视为更专业的 POCO,但它们所拥有的只是属性以及用于转换数据类型或其他基本非业务实用程序功能的助手。为了更具体地了解第一个项目,实体框架模型应转换为 Dto/POCO 或域对象。您不希望 IQueryable 之类的东西在存储库之外运行松散。当我说将域对象转换为 ViewModels 时,它基本上意味着获取您的员工对象和任何其他需要的域对象并创建一个 ViewModel 对象。
    • 您可以简单地使用您的域对象作为您的视图模型并将其发送到视图,但是,域对象并不总是对齐。特别是如果您有多个域实体构成您的视图。域实体和 Dto 之间唯一真正的区别在于它们封装了特定于实体的业务逻辑。
    • -Repository 包装 EntityFramework -Repository 返回并接受 Dto 的 -Unit of Work 调用 Repository 并将 Dto 转换为 Domain Objects -Controller 调用 Unit of Work 并将 Domain 对象转换为 ViewModel -View“绑定”到 ViewModel展示。警告 - 你并不总是需要所有这些。取决于您的应用大小以及您希望与其他应用重复使用多少。
    猜你喜欢
    • 2011-04-20
    • 1970-01-01
    • 1970-01-01
    • 2016-12-18
    • 2012-07-16
    • 2011-05-30
    • 1970-01-01
    • 2011-06-28
    • 1970-01-01
    相关资源
    最近更新 更多