56

我刚刚开始使用实体框架,我对业务层中的类通常如何与实体框架创建的实体相适应感到困惑。

在使用经典的 ADO.NET 时,我将有一个名为 Customer 的类,然后是另一个名为 DALCustomer 的类来处理数据库交互,在这种结构中,我将代码用于计算、过滤和删除 DAL withing 的实例Customer 用于在 Customer 类中保存、更新和删除。

使用实体框架,如果您有一个名为 Customer 的表,实体框架会创建一个名为 Customer 的实体,这就是我开始困惑的地方,这个实体是否消除了业务层中对 Customer 的需求?那么本质上,通常在业务层中的所有字段和方法都在实体框架生成的实体中?还是应该在业务层中仍然存在一个名为 CustomerBL 的类,例如,它仍然包含完成计算、过滤所需的业务逻辑所需的字段和方法,并且仍然需要声明的 EF DAL 实例来处理数据访问?

如果应该有一个业务类,在这种情况下是 CustomerBL,另一个问题会跳到脑海中,是否应该在 CustomerBL 中重新创建在客户实体中创建的字段,或者是否应该在 CustomerBL 中声明客户实体的实例,这样就会有不需要在 2 个位置声明字段?

4

5 回答 5

27

实体框架的设计考虑了数据模型和概念模型之间的分离。它支持继承实体拆分(不是 EF 核心)、表拆分复杂类型拥有类型以及透明的多对多关联(不是 EF 核心),所有这些都允许根据自己的需要塑造领域模型而不受约束很大程度上取决于数据存储模型。EF 核心支持影子属性,可用于隐藏暴露的类模型的横切关注点。

代码优先方法允许使用 POCO,其中只有少数属性映射到数据存储列,而其他属性则服务于域目标。模型优先和数据库优先生成部分类,允许扩展生成的代码。

当然,这种概念模型和商店模型的分离只能在一定程度上取得成功。有些事情与坚持无知的目标背道而驰。例如 -

  • 如果需要延迟加载,则需要将导航属性声明为virtual,以便 EF 可以在代理类型中覆盖它们。领域驱动设计 (DDD) 将鼓励virtual仅在需要多态性时使用。

  • 伴随“真实”关联(参考)的原始外键属性(例如 )非常方便。纯粹主义者认为这违反了 DDD 原则。ParentIdParent

  • EF 类模型将是数据访问层的一部分,并且应该主要服务于该目标。因此,它将包含许多互惠关系,以便在编写 LINQ 查询时尽可能地受益于导航属性。这些相互关系是对 DDD 原则的又一次违反。

  • LINQ-to-objects 和 LINQ-to-entities 之间存在大量差异。您不能忽略这样一个事实,即您正在针对与内存中的对象完全不同的宇宙进行 LINQ-ing。这被称为紧密耦合或泄漏抽象

  • EF 只能映射具体类,不能映射接口。

但是……通常我很乐意将代码优先模型中生成的 EF 类或 POCO 用作域类。到目前为止,我从未见过从一个数据存储或 ORM 到另一个的无摩擦过渡,如果它发生的话。执着无知是虚构的。DAL 的特质很容易在领域中留下足迹。只有当您必须为不同的数据存储/模型编写代码时,或者当存储/模型预计会相对频繁地更改时,才需要尽可能减少这种占用空间或将其完全抽象出来。

另一个可能将 EF 类提升为域类的因素是当今许多应用程序具有多个层,其中(序列化)不同的视图模型或 DTO 被发送到客户端。在 UI 中使用域类几乎不符合要求。您也可以将 EF 类模型用作域,并让服务根据 UI 或服务使用者的要求返回专用模型和 DTO。如果只是在性能方面,另一个抽象层可能更像是一种负担而不是一种祝福。

于 2013-01-15T20:05:28.597 回答
15

在我看来,使用 POCO 作为可以持久化的实体的全部意义在于消除“数据库实体”和“业务实体”之间的区别。“实体”应该是“业务实体”,可以直接保存到数据存储并从数据存储中加载,因此同时充当“数据库实体”。通过使用 POCO,业务实体与特定机制分离以与数据库交互。

您可以将实体移动到一个单独的项目中 - 例如 - 该项目没有对任何 EF 程序集的引用,但在数据库层项目中使用它们来管理持久性。

这并不意味着您可以在不考虑 EF 要求的情况下完全设计您的业务实体。当您使用 EF 将业务实体映射到数据库模式时,您需要了解一些限制以避免麻烦,例如:

  • 您必须创建导航属性(对其他实体的引用或引用集合)virtual以支持使用 EF 进行延迟加载
  • 您不能IEnumerable<T>用于必须持久化的集合。它必须是ICollection<T>或更派生的类型。
  • private持久化属性并不容易
  • EF 不支持该类型char,如果要保留其值,则不能使用它
  • 和更多...

但是,在我看来,另外一组实体是额外的复杂层,如果上述限制对您的项目来说太严格,则应该有理由真正需要它。

YA2C(又是 2 美分 :))

于 2013-01-15T20:16:46.073 回答
5

我不知道其他人是否认为这是一种好的做法,但就个人而言,这就是我过去处理这个问题的方式:

EF 生成的类是您的 DAL,然后为 BL 创建一组互补的类,您将在其中拥有所需的结构(例如可能以一对一的关系合并来自相关实体的数据)并处理其他业务逻辑问题(自定义验证,例如实现 IDataErrorInfo 以使其与 WPF 中的 UI 配合使用)并且还创建包含与实体类型相关的所有业务层方法的类,这些方法使用 BL 实例并与 EF 实体相互转换到 BL 对象。

因此,例如,您的数据库中有客户。EF会生成一个类Customer,在BL中会有一个Customer(前缀、后缀等)类和一个CustomerLogic类。在 BL Customer 类中,您可以做任何需要满足需求的事情,而不必篡改 EF 实体,在 CustomerLogic 类中,您将拥有 BL 方法(加载最有价值的客户、为客户保存额外的数据等)。

现在,这使您能够松散地耦合到数据源实现。过去(在 WPF 项目中)这使我受益的另一个示例是,您可以执行 IDataErrorInfo 之类的操作并在 CustomerBL 类中实现验证逻辑,这样当您将实体绑定到 UI 上的创建/编辑表单时您将受益于 WPF 提供的内置功能。

...我的 2 美分,我也很想知道什么是最佳实践或其他解决方案/观点是什么。


也可能与这个主题有关 -代码优先与模型/数据库优先

于 2013-01-15T18:06:35.127 回答
3

这个话题可能有点老了,但这可能会有所帮助。Andras Nemes 在他的博客中指出了使用 DDD(领域驱动设计)而不是 EF、MVC 等技术驱动设计的担忧。

http://dotnetcodr.com/2013/09/12/a-model-net-web-service-based-on-domain-driven-design-part-1-introduction/

于 2014-03-28T13:11:05.017 回答
0

我使用业务逻辑来编写我的方法并在其创建的视图中返回结果,例如:

namespace Template.BusinessLogic
{
    public interface IApplicantBusiness
    {
        List<Template.Model.ApplicantView> GetAllApplicants();

        void InsertApplicant(Template.Model.ApplicantView applicant);
    }
}
于 2014-07-22T21:19:19.027 回答