ef核心最佳实践来更新复杂对象

时间:2019-12-10 10:37:30

标签: c# sql api asp.net-core entity-framework-core

当尝试在客户端提供的数据库上更新复杂对象时,我们会在Web-api中导致sql数据库上的ef-core出现一些问题。

一个详细的示例:当从客户端收到带有1-n个“帖子”的对象“ Blog”并尝试更新数据库上的该现有对象时,我们应该:

  1. 确保已设置主键并可以使用 dbContext.Update(blogFromClient)
  2. 同时加载和跟踪博客 包括数据库中的帖子,然后修补来自 客户到该对象上并使用SaveChanges()

使用方法(1)时,我们遇到以下问题:

  1. 数据库中现有博客的现有帖子未删除 当客户端不再发布它们时,需要手动 找出并删除它们

  2. 遇到跟踪问题("is already been tracked"),如果 博客的依赖项(例如,“用户”为“创建者”) 已经在ChangeTracker

  3. 在不使用真实的DbContext的情况下无法对我们的业务逻辑进行单元测试 使用存储库模式时(不存在跟踪错误)

  4. 虽然将真实的DbContextInMemoryDatabase一起使用进行测试,但不能依赖于外键异常或计算式 列

使用方法(2)时:

  1. 我们可以轻松管理更新的关系并轻松跟踪 对象
  2. 由于加载了 我们不需要的对象
  3. 需要映射许多手动内容 因为AutoMapper之类的工具无法用于自动映射 具有n-n关系的对象,同时通过ef core保持正确的跟踪(会得到一些主键错误,因为某些对象已从列表中删除,并再次使用相同的主对象添加 键,这是不允许的,因为不能在插入时设置主键)
  4. 可以很容易地破坏
  5. n-n关系,就像在数据库上一样 可能有n-n个博客要发布,而博客中的帖子确实有效 与其职位相同的关系。如果只有一种关系是( 发布,但不发布到博客-在sql中是相同的)并发布 另一部分已从列表中删除,ef核心将跟踪该条目 为“已删除”。

在普通SQL中,我们将通过

进行管理
  1. 删除博客的所有现有关系以发布帖子
  2. 更新帖子本身
  3. 创建所有新关系

在ef核心中,我们无法编写诸如删除批量关系的语句,而无需先加载它们,然后再详细跟踪每个关系。

是否有最佳实践,如何在从客户端获取“新”数据的同时处理具有深层关系的复杂对象的更新?

1 个答案:

答案 0 :(得分:1)

正确的方法是#2:“加载并跟踪博客,同时包括来自数据库的帖子,然后将来自客户端的更改修补到该对象上并使用SaveChanges()”。

关于您的担忧:

  

由于加载了我们不需要的对象而导致性能下降

您认为不需要此信息是不正确的。实际上,您确实需要这样做,因为您绝对不应该在每个实体和相关实体上发布每个属性,包括不应更改的内容(例如审核道具等)。如果您发布每个媒体资源,那么保存时最终会清空内容。因此,唯一正确的路径是始终从数据库加载完整的数据集,然后通过发布的内容对其进行修改。用其他任何方式 都会引起问题,并且完全是100%错误。

  

需要映射许多手动的东西,因为AutoMapper之类的工具无法用于自动映射具有n-n关系的对象,同时无法通过ef core保持正确的轨迹

您在此描述的是任何自动映射的局限性。为了将实体映射到集合中的实体,该工具必须以某种方式知道唯一标识每个实体的内容。当然,通常这通常是PK,但是AutoMapper不会(也不应该)对此进行假设。相反,默认和幼稚的行为是将目标上的集合替换为源上的集合。但是,对于EF来说,您似乎要删除集合中的所有内容,然后将新项目添加到集合中,这就是问题的根源。

前进有两条路。首先,您可以简单地忽略源上的收集道具,然后手动将其映射。您仍然可以使用AutoMapper进行映射,但是您只需要根据了解实体的知识(即AutoMapper零件),遍历集合中的每个项目,使其分别与应映射到的适当项目进行匹配即可。不知道。)

第二,实际上还有一个供AutoMapper使用的附加库,可简化此操作:AutoMapper.Collection。该库的全部要点是能够告诉AutoMapper如何标识您的实体,以便它可以正确地映射集合。如果您使用此库并添加其他必要的配置,则可以正常映射实体,而不必担心集合会混乱。