【问题标题】:Entity Framework with 3-tier architecture, different entities across domains具有 3 层架构的实体框架,跨域的不同实体
【发布时间】:2014-12-18 16:18:28
【问题描述】:

我知道标题听起来像是很多现有帖子的副本,但我已经阅读了很多帖子,而我的情况实际上完全不同。如果任何有 Entity Framework 经验的人可以就以下场景的最佳架构提供一些建议,我将不胜感激。

我有一个具有 3 层布局、数据访问层、业务逻辑层和 UI 表示层的 wpf 应用程序。 UI 使用 MVVM。 DAL 使用实体框架。 UI 和数据访问层各有自己的模型,即 UIModel 和 DataModel。

当前设计在整个应用程序中为实体框架使用全局 DbContext。对于简单的更新操作,从数据库中检索实体作为 DataModel,转换为 GUIModel,连接到 ViewModel 和 View 以进行更新,然后转换回 DataModel 以在数据库中更新。这就是问题所在:当从转换中创建新的 DataModel 时,它不再与检索到的原始实体相关,并且 Entity Framework 无法执行更新,因为它现在有两个相同主键的重复模型附加到同一个 DbContext .

我做了一些研究,发现了几种可能的方法来解决这个问题。一种是跨所有层使用单个模型实体,而不是将 GUIModel 和 DataModel 分开,并将全局 DbContext 分解为工作单元。这似乎是一个很常见的设计,但我对这种做法的担忧是 GUIModel 和 DataModel 的合并违反了职责分离,并且使用工作单元需要 Business Layer 来控制 DbContext 的生命周期,这也模糊了两者之间的界限BLL 和 DAL。

第二种选择是为每个带有using 块的数据库查询使用本地 DbContext。这似乎是最节省内存的。但是这样做会使延迟加载变得不可能,并且在每个查询中急切加载所有导航属性可能会影响性能。此外,短暂的 DbContexts 需要完全在断开连接的图中工作,这在更改跟踪方面变得相当复杂。

第三种可能性是缓存所有原始数据模型并在更新后更新这些实体。

我是 Entity Framework 的新手,我相信应该还有其他方法来解决这个问题。如果有人能就解决此问题的最佳方法提供一些见解,我将不胜感激。

【问题讨论】:

    标签: asp.net wpf entity-framework mvvm n-tier-architecture


    【解决方案1】:

    更好的方法是,当您要在存储库中进行更新调用时,首先通过主键获取实体,现在您在 dbContext 中需要更新的实体,然后分配更新的字段并更新上下文。

    代码如下:

    public void UpdateEntity(Entity updatedEntity)
        {
            using (var db = new DBEntities())
            {
                var entity = db.Entities.Find(updatedEntity.Id);
                if (entity!= null)
                {
                    entity.Name = updatedEntity.Name;
                    entity.Description = updatedEntity.Description;
                    entity.LastModifiedBy = updatedEntity.LastModifiedBy;
                    entity.Value = updatedEntity.Value;
                    entity.LastModifiedOn = DateTime.Now;
                    db.SaveChanges();
                }
            }
         }
    

    【讨论】:

      【解决方案2】:

      我建议您使用第二个备选方案中描述的单独业务对象。在多层场景中,您将创建从 UI 角度支持您的用例的可重用对象,对您的业务域的行为进行建模(您称它们为“GUIModel”)。这些模型应该关注系统的行为,并且只包含支持这种行为所需的数据。这与专注于数据的实体类形成鲜明对比。

      示例:罗斯文数据库、客户表。该实体将是一个包含客户所有属性的类,可能具有相关事物的导航属性。当您需要在自动完成搜索框的下拉列表中显示精简的客户信息列表时,您真的想使用此模型吗?您是否希望使用相同的模型在网格中显示客户及其汇总的发票数据?您需要将所有客户信息与相关发票一起加载到您的表示层。你可能不想这样做。

      如果您有针对不同用例的不同模型,那么从面向对象的角度来看,事情会更有意义:

      CustomerSearchResult:ID,名称。 GetCustomerEdit 方法。

      CustomerInvoiceInfo 类:ID、名称、汇总发票值。 GetCustomerEdit 方法。

      Class CustomerEdit:您要显示和编辑的所有属性,乐观并发检查的时间戳。更改跟踪逻辑、验证逻辑。在编辑客户时对您需要的行为进行建模的方法。

      CustomerEntity 类:这是类似于客户表的数据对象。您将其用作 DTO 来初始化数据库中的其他对象或将更改推送回数据库。你不会通过网络发送它。

      这样,当您到达数据访问层时,您可以将您的 DbContext 放入 using 块中并尊重工作单元模式。当然,您需要通过创建一个新的CustomerEntity 来反映对CustomerEdit 实例所做的更改,并将其重新附加到修改后的上下文中:

      context.Entry(entity).State = EntityState.Modified;
      context.SaveChanges();
      

      一开始这似乎很复杂且繁重,但实际上,Entity Framework 不包含任何可以在断开连接(n 层)场景中帮助您的魔法。如果您尝试使用延迟加载或始终保持 DbContext 实例打开之类的方法,那么事情很快就会失控。

      如果您正在寻找有助于创建业务对象并支持多层架构的框架,请查看 CSLA.net。免责声明:这里的很多人不喜欢它。如果使用不当,情况会变得更糟。尽管如此,它在一些项目中帮助了我,我对此很满意。

      【讨论】:

        【解决方案3】:
        1. 您可以通过使用 以下代码,here 也是来自 MSDN 的关于实体状态的好帖子 var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" }; 使用 (var context = new BloggingContext()) { context.Entry(existingBlog).State = EntityState.Modified; // 做更多的工作...
          context.SaveChanges(); }
        2. 关于 3 层,我想先用 .net 上下文对每一层进行小描述

          • Presentation,这是将结果返回给用户的层,它可以是 ASP.Net 网站、Windows 窗体、Web Api、WCF 服务或其他任何形式
          • 业务,这应包括您的业务的域模型、业务逻辑和跨多个域实体提供业务的服务
          • 数据访问/持久化,该层应包括将域模型持久化并将其检索到持久媒体(如数据库、文件系统等)中的逻辑...

          一般来说,这里的常见问题是哪个模型进入哪个层,例如 X 类应该进入演示或业务,我推荐一种简单的方法来帮助您做出决定,即引入新的同级层,所以问问自己是否愿意喜欢构建另一个表示层作为控制台而不是窗口,您会将该逻辑复制并粘贴到新层中吗?如果是,那么您的课程很可能不在正确的位置。

        最后是一些具体的建议,

        • 保持每一层都有自己的模型,因为每一层都有唯一的 责任,也有很好的框架可以帮助你 AutoMapper等模型之间的映射
        • 不要跨层传输 Entity Framework 模型,因为这会破坏关注点的分离,而且如果启用延迟加载,它会出现越来越多的问题。
        • 尽量避免延迟加载,除非您知道自己在做什么,常见的陷阱之一是 Select N+1,here 是描述它的好文章。
        • 另外,如果您有复杂的业务,请尝试通过应用CQRS 模式将查询系统和更新系统分开,并且有一些框架可以帮助您,例如Dapper

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-03-16
          • 2023-03-27
          • 2018-04-02
          • 1970-01-01
          • 2014-03-23
          • 2015-11-28
          相关资源
          最近更新 更多