当尝试在客户端提供的数据库上更新复杂对象时,我们会在Web-api中导致sql数据库上的ef-core出现一些问题。
一个详细的示例:当从客户端收到带有1-n个“帖子”的对象“ Blog”并尝试更新数据库上的该现有对象时,我们应该:
dbContext.Update(blogFromClient)
SaveChanges()
使用方法(1)时,我们遇到以下问题:
数据库中现有博客的现有帖子未删除 当客户端不再发布它们时,需要手动 找出并删除它们
遇到跟踪问题("is already been tracked"
),如果
博客的依赖项(例如,“用户”为“创建者”)
已经在ChangeTracker
在不使用真实的DbContext
的情况下无法对我们的业务逻辑进行单元测试
使用存储库模式时(不存在跟踪错误)
虽然将真实的DbContext
与InMemoryDatabase
一起使用进行测试,但不能依赖于外键异常或计算式
列
使用方法(2)时:
AutoMapper
之类的工具无法用于自动映射
具有n-n关系的对象,同时通过ef core
保持正确的跟踪(会得到一些主键错误,因为某些对象已从列表中删除,并再次使用相同的主对象添加
键,这是不允许的,因为不能在插入时设置主键)在普通SQL中,我们将通过
进行管理在ef核心中,我们无法编写诸如删除批量关系的语句,而无需先加载它们,然后再详细跟踪每个关系。
是否有最佳实践,如何在从客户端获取“新”数据的同时处理具有深层关系的复杂对象的更新?
答案 0 :(得分:1)
正确的方法是#2:“加载并跟踪博客,同时包括来自数据库的帖子,然后将来自客户端的更改修补到该对象上并使用SaveChanges()
”。
关于您的担忧:
由于加载了我们不需要的对象而导致性能下降
您认为不需要此信息是不正确的。实际上,您确实需要这样做,因为您绝对不应该在每个实体和相关实体上发布每个属性,包括不应更改的内容(例如审核道具等)。如果您不发布每个媒体资源,那么保存时最终会清空内容。因此,唯一正确的路径是始终从数据库加载完整的数据集,然后通过发布的内容对其进行修改。用其他任何方式 都会引起问题,并且完全是100%错误。
需要映射许多手动的东西,因为
AutoMapper
之类的工具无法用于自动映射具有n-n关系的对象,同时无法通过ef core
保持正确的轨迹
您在此描述的是任何自动映射的局限性。为了将实体映射到集合中的实体,该工具必须以某种方式知道唯一标识每个实体的内容。当然,通常这通常是PK,但是AutoMapper不会(也不应该)对此进行假设。相反,默认和幼稚的行为是将目标上的集合替换为源上的集合。但是,对于EF来说,您似乎要删除集合中的所有内容,然后将新项目添加到集合中,这就是问题的根源。
前进有两条路。首先,您可以简单地忽略源上的收集道具,然后手动将其映射。您仍然可以使用AutoMapper进行映射,但是您只需要根据了解实体的知识(即AutoMapper零件),遍历集合中的每个项目,使其分别与应映射到的适当项目进行匹配即可。不知道。)
第二,实际上还有一个供AutoMapper使用的附加库,可简化此操作:AutoMapper.Collection
。该库的全部要点是能够告诉AutoMapper如何标识您的实体,以便它可以正确地映射集合。如果您使用此库并添加其他必要的配置,则可以正常映射实体,而不必担心集合会混乱。