当使用实体框架作为数据访问层时,如何实现业务逻辑层?

Posted

技术标签:

【中文标题】当使用实体框架作为数据访问层时,如何实现业务逻辑层?【英文标题】:How do you implement a business logic layer when using entity framework as data access layer? 【发布时间】:2012-08-18 03:22:12 【问题描述】:

假设我有 3 层

第 1 层:WPF 项目(包含:xaml、viewmodels、mvvm 框架)

第 2 层:业务逻辑层(包含:普通 c# 类)

第 3 层:实体框架(包含:存储库/数据访问类)

当您不想将 DAL(第 3 层)暴露给 View(第 1 层)时 你如何很好地实现业务逻辑层? 我很难在 BLL 对象和 DAL 实体之间来回传递值。

你能帮忙吗?

【问题讨论】:

我将热切地关注这个线程,但我的理解是您将业务逻辑放入模型类中。您的模型类依赖于您的存储库 (DAL) 类,而这些类又使用 EF 访问数据库。因此,由于您的 WPF 视图应该依赖于您的模型,因此我看不出您的视图中如何需要依赖于您的 DAL。也就是说,我使用 MVC 方式比 WPF 的 MVVM 模式更频繁,所以也许我遗漏了一些东西。 @RobertChrist MVVM 与 MVC 有点不同。在 MVVM 中,模型只是简单的数据对象,而视图模型应该处理应用程序/业务逻辑和数据访问。 【参考方案1】:

实体框架在“断开连接”的模型中不能很好地工作。除非有什么改变,否则我永远无法轻松地让它很好地工作。

您可以在第 2 层中使用 AutoMapper 创建 ViewModel 以呈现给第 1 层,但是您仍然必须将更改发送回第 3 层,这可能会很麻烦。

【讨论】:

【参考方案2】:

实体框架本身应该用作“逻辑层”。

你的想法可能有点道理,使用组合技术。

但是,我认为其中一些 Micros。设计为单独使用的库,除非在其网站中明确合并和提及。

祝你好运。

【讨论】:

【参考方案3】:

无需将所有 DAL 暴露给视图层,只需在所有层之间交换域对象(EF 对象)即可。结构可能类似于以下:

    表示层-------- ^ BLL --------------- |数据对象 DAL(存储库等)--- |

因此,您的所有层都将断开连接;但它们将共享相同的域对象。在现实世界中,您可以通过为所有层共享的实体框架实体创建单独的 dll 来实现类似的结构。请注意,ObjectContext 应该只对 DAL 可见(默认情况下,实体和对象上下文是在同一个 dll 中生成的,您需要将其分成两个 dll)。

【讨论】:

建议不要让 EF 实体(甚至 POCO)跨越数据层边界,即业务对象!= EF 对象。 @voroninp 不建议这样做是对的。但我认为这取决于项目的要求(您最终可能会遇到过度设计的架构)。 @voroninp - POCO 类可以是业务对象...例如,Ladislav Mrnka (***.com/questions/5688141/…) 甚至推荐使用 POCO ar 业务对象 @Prokurors 是的,你是对的。我们自己现在也是这样。 DTO 暴露在服务合同中。我们在 POCO 中注入依赖项也遇到了一些困难。【参考方案4】:

您的ViewModels 是您在 MVVM 中的应用程序,因此它们应该通过您的存储库类或业务逻辑(直接或间接使用您的验证类)处理诸如数据访问之类的事情。

如果您不想直接引用这些类,请使用Interface,但请注意,您需要通过某种方式将继承的类传递给 ViewModel。我曾经做过一个项目,其中我们有一个共享接口库,例如 IRepository 接口,并且所有内容都是使用 MEF 导入/导出的,因此这些层不会直接相互引用。

最终的(简化的)布局是:

模型 -> 包含可以对自己的数据(长度、最小值/最大值等)进行简单验证但不进行任何高级验证或业务的纯数据对象逻辑。没有引用任何其他库。

Common -> 包含的接口、实用程序和其他常用共享类。仅引用了模型库。

DAL -> 基于通用库​​中的存储库接口包含的数据访问和存储库。引用了接口定义和实用程序的通用库,以及模型库,因为它包含数据访问调用中使用或返回的数据模型

ViewModels -> 包含应用程序和业务逻辑。引用 Common 的实用程序和接口,以及数据模型的模型

客户端 -> 包含视图。引用模型和视图模型,因此它可以为这两种对象类型编写 DataTemplates

【讨论】:

我不同意 ViewModel 是您的应用程序。你当然可以把它们变成那样,但这并不适合非常便携的业务逻辑。 @KaseySpeakman 嗯,BL 不是一个坚实的结构。你可以把它分成不同的方面。 BL 的某些部分具有代表性。由虚拟机实现时没有错。但当然,任何可重用的逻辑都应该放在其他地方。 @voroninp 在 UI 中实现业务逻辑是不好的做法。它有效,但它为技术债务奠定了坚实的基础。 @KaseySpeakman。应该选择或什至关注哪个项目可能非常依赖于业务逻辑。从 UI 中完全抽象出 BL 是不可能的。还有一些交叉问题,例如验证和授权。 ViewModel 不是 UI。最好将 VM 视为考虑 UI 类型的 BL。 横切关注点也应该从 VM 中分离出来,以便它们可以以横切方式应用(不仅仅是在 VM 中)。同意不同意在 ViewModel 中编写 BL 代码。但是,嘿,这是您需要支付的代码维护费用。【参考方案5】:

除了 Daryal 的回答之外,您还可以将 BL 中的业务对象定义为 POCO,并在 DAL 层中使用 EF 从数据库中读取它们或将它们保存到数据库中。

如果您在您的 BL(或工作单元,无论您给它的命名如何)中定义了一个类似存储库的接口,并在您的 DAL 中实现该接口,那么您只需要一点 DI 即可获得存储库类,而不必一直引用 DAL。它对我来说就像一种魅力。

请参阅here 和here 了解更多信息。

【讨论】:

感谢马丁!我确实尝试将 POCO 放入 BLL .. 感谢您的链接!我会看看他们。【参考方案6】:

如果您只是开发一个在可预见的情况下不会改变的小应用程序,那么现在就做最快的事情。如果您想要一个长期可维护的应用程序,那么让我提出以下内容。

这种垂直层方法的问题在于,当您需要视图中的某些内容时,您必须将其添加到业务层(即使业务层不关心它)和数据层。这要求您的业务层看起来像 1) 数据的 UI 视图 2) 数据的业务视图和 3) 数据的数据库表示。那和/或您需要在层之间进行大量映射。所有这些都淡化了业务层表示业务逻辑的实际目的。然后往往会发生的是,所有业务逻辑都被迁移到事务方法(也可以是静态方法)中,以仅更改数据对象的状态。如果您的问题域只是没有复杂逻辑的 CRUD,那完全没有错。对于复杂的逻辑,此类业务方法可能会变得非常棘手,甚至会产生不确定的结果(因为“字段”上复杂的相互依赖性可能是矛盾的且难以解决)。

假设您有复杂的逻辑,数据的用户视图和数据的业务表示通常是非常不同的,因此 UI 视图最终具有数据的专门模型。如果我是你,我会接受这一点并使用 CQRS 原则的简单版本。也就是说,让您的 UI 视图的数据来自与执行业务操作的地方不同的地方。在您的情况下,我可能在您的 DAL 中有一个 EF 模型,它只为您的 UI 视图提供服务,并准确地为它们提供所需的东西(无论是通过数据库视图、存储过程还是预编译的报告表)。然后创建一个单独的 EF 模型,该模型仅服务于您的业务实体的需求。然后,当您准备好执行实际业务操作时,UI 的 viewmodel 操作方法可以从业务 EF 模型加载业务对象(或者更好的是,从执行此操作的业务层调用对象工厂)并运行适当的操作业务对象。请注意,我在这里还假设您的数据库是高度相关的(主要是第 2 和第 3 范式)并且您已准备好使用 EF。

我不会尝试将您的视图模型变成业务逻辑。这使得您的业务逻辑不容易在其他平台或应用程序(如 Web)上重用。 MVVM 应该只服务于 UI。在我看来,V(视图)代表用户看到和操作的内容。 M(模型)代表用户在该视图中选择的内容。 VM(视图模型)在两者之间进行转换。然后,您的程序应采用用户经过验证的选择(UI 模型)并从中提取所需的数据以执行适当的业务操作。

【讨论】:

【参考方案7】:

我第二个 daryal,我已经在一些项目中实现了这一点。使用 POCO 作为业务对象是绝对可以的。

另一种方法不是创建 2 个项目(一个用于 DB 上下文,一个用于实体)是仅使用一个 DAL/DATA 项目并在 BLL 中引用它。然后继承 BLL 中的 POCO 类,并在 UI 层使用该类。以下是基本示例代码:

// assume this is EF generated POCO
namespace Owner.Project.Data
    public partial class Product
    
        public int ProductId  get; set; 
        public string ProductName  get; set; 
    


// this is manually created class in BLL
using Owner.Project.Data;
namespace Owner.Product.Business
    public class StoreProduct : Product 
        public bool Add (string productName)
            // TODO - add product using EF as you normally do
        
    

现在您可以在您的 UI 项目中引用 BLL 并在那里使用 StoreProduct 类。要实现松耦合模式,当然也可以从 IStoreProduct 接口继承 StoreProduct 类,并在 UI 或 Web 服务中使用它,但这是另一个话题。

【讨论】:

以上是关于当使用实体框架作为数据访问层时,如何实现业务逻辑层?的主要内容,如果未能解决你的问题,请参考以下文章

SSH框架

使用实体框架时对将业务逻辑放在哪里感到困惑

任何将单独的数据访问层(使用 EF)突出显示到业务逻辑层的示例 C# 项目

在业务逻辑层使用实体框架生成的类

N 层服务层验证显示表示层中的业务逻辑错误

ASP WebApi:服务层、业务层和数据访问层