记录一次EF实体跟踪错误
记录一次EF实体跟踪错误
前言
在我写文章编辑接口的,出现了一个实体跟踪的错误,详情如下
System.InvalidOperationException: The instance of entity type 'Tag' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.
System.InvalidOperationException:无法跟踪实体类型“Tag”的实例,因为已跟踪具有相同 {'Id'} 键值的另一个实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。考虑使用“DbContextOptionsBuilder.EnableSensitiveDataLogging”来查看冲突的键值。
代码
这是控制器,这个是用于修改文章的一个接口
[HttpPut("{id}")]
public async Task<ApiResponse<Post>> Update(string id, PostUpdateDto dto)
{
var post = _postService.GetById(id);
if (post == null) return ApiResponse.NotFound($"博客 {id} 不存在");
post = _mapper.Map(dto, post);
post.LastUpdateTime = DateTime.Now;
return new ApiResponse<Post>(await _postService.InsertOrUpdateAsync(post));
}
PostUpdateDto类如下:
public class PostUpdateDto
{
public string Id { get; set; }
/// <summary>
/// 标题
/// </summary>
public string Title { get; set; }
/// <summary>
/// 梗概
/// </summary>
public string Summary { get; set; }
/// <summary>
/// 内容(markdown格式)
/// </summary>
public string Content { get; set; }
/// <summary>
/// 分类ID
/// </summary>
public int CategoryId { get; set; }
/// <summary>
/// 标签
/// </summary>
public List<Tag> Tags { get; set; }
}
关键在于public List<Tag> Tags { get; set; }
这里,我使用了Map去让PostUpdateDto可以映射为post文章类,然后Tags是前端传过来的标签集合,同时也是post的导航属性,控制器是没毛病的,继续往下看。
然后是InsertOrUpdateAsync方法:
传递一个post实体对象,然后对实体进行增改操作,我按照常规的写法,修改标签,肯定是要将文章之前的标签删除掉,然后重新添加,这是正常的流程。PostTags就是标签表,用于维护标签和文章的关系,删除文章标签实际就是删除PostTags表的内容。
可以看到我删除了existingTags查询出的对象,也就是删除当前文章下的标签。然后通过遍历前端传过来的tags对象新增新的书签,之后就是保存到数据库的操作了。流程是没有问题的,但是每次编辑就报Tags实体跟踪错误
public async Task<Post> InsertOrUpdateAsync(Post post)
{
using var transaction = await _myDbContext.Database.BeginTransactionAsync();
try
{
// 是新文章的话,先保存到数据库
if (await _myDbContext.posts.FindAsync(post.Id) == null)
{
post.ViewCount = 0;
await _myDbContext.posts.AddAsync(post);
await _myDbContext.SaveChangesAsync();
}
var tags = post.Tags.ToList();
// 移除原有的标签,没写引发异常
// post.Tags.Clear();
var existingTags = await _myDbContext.PostTags
.Where(pt => pt.PostId == post.Id)
.ToListAsync();
_myDbContext.PostTags.RemoveRange(existingTags);
//修改
foreach (var tagName in tags)
{
var postTag = new PostTag
{
PostId = post.Id,
TagId = tagName.Id,
};
_myDbContext.PostTags.Add(postTag);
}
// 保存更改到数据库
await _myDbContext.SaveChangesAsync();
// 检查文章中的外部图片,下载并进行替换
post.Content = await MdExternalUrlDownloadAsync(post);
// 修改文章时,将markdown中的图片地址替换成相对路径再保存
post.Content = MdImageLinkConvert(post, false);
// 更新文章
_myDbContext.posts.Attach(post);
_myDbContext.Entry(post).State = EntityState.Modified;
await _myDbContext.SaveChangesAsync();
// 提交事务
await transaction.CommitAsync();
return post;
}
错误在开头就说了,无法跟踪实体类型“Tag”的实例,我琢磨的很久,我明明没有查询Tag,也没有删除Tag,我删除的是PostTags表,为什么会报这个错误。而且我是没有做外键约束的。
解决
找了很久,发现了一个致命的漏点,我传递的post对象是一个导航属性的Tags的,如图:
可以看到Tags里面是有PostTags的,所以当我获取所有标签var tags = post.Tags.ToList();
同时删除PostTags中的内容的时候_myDbContext.PostTags.RemoveRange(existingTags)
然后通过循环 foreach (var tagName in tags)
添加了新的标签,但是我并没有将这些标签从 post.Tags
集合中移除。因此,当我保存更改到数据库时,EF Core 会认为 post.Tags
集合中的标签实例已经被跟踪,导致了异常。
简单来说就是post中的Tags要被清除才行,也就是这行代码必须要写上去
// 移除原有的标签
post.Tags.Clear();
结论
当对具有导航属性的数据进行增删改的时候,先看看实体中是否存在这些数据,记得先Clear再执行操作。在Clear之前可以将需要的数据保存到变量中,因为Clear之后,是无法通过实体.(点)出属性了
记录一次EF实体跟踪错误的更多相关文章
- EF实体框架之CodeFirst一
对于SQL Server.MySql.Oracle等这些传统的数据库,基本都是关系型数据库,都是体现实体与实体之间的联系,在以前开发时,可能先根据需求设计数据库,然后在写Model和业务逻辑,对于Mo ...
- EF实体框架之CodeFirst四
在EF实体框架之CodeFirst二中也提到数据库里面一般包括表.列.约束.主外键.级联操作.实体关系(E-R图).存储过程.视图.锁.事务.数据库结构更新等.前面几篇博客把表.存储过程.视图这些算是 ...
- 实习笔记-3:ef实体操作错误篇
学习笔记 1.json序列化ef实体是报错:“序列化类型为“System.Data.Entity.DynamicProxies.XXXX.... 对象时检测到循环引用.” 公司里用ef来生成实体.但是 ...
- 匿名类型 使用泛型T linq返回dynamic类型的匿名实体 如何把匿名类型.GetType()返回的对象传进泛型里面 EF实体查询出的数据List<T>转DataTable出现【DataSet 不支持 System.Nullable<>】的问题
[100分]紧急求助:LinQ下使用IQueryable<T>如何将返回类型<T>使用匿名类型 问题描述如下:我有一个方法如下:public IQueryable Dissen ...
- 【MVC 1】MVC+EF实体框架—原理解析
导读:在之前,我们学过了三层框架,即:UI.BLL.DAL.我们将页面显示.逻辑处理和数据访问进行分层,避免了一层.两层的混乱.而后,我们又在经典三层的基础上,应用设计模式:外观.抽象工厂+反射,使得 ...
- BIM工程信息管理系统-EF实体框架数据操作基类
EF实体框架数据操作基类主要是规范增.改.查.分页.Lambda表达式条件处理,以及异步操作等特性,这样能够尽可能的符合基类这个特殊类的定义,实现功能接口的最大化重用和统一. 1.程序代码 /// & ...
- EF非常见错误:EXECUTE 后的事务计数指示 BEGIN 和 COMMIT 语句的数目不匹配
EF非常见错误:EXECUTE 后的事务计数指示 BEGIN 和 COMMIT 语句的数目不匹配 问题原因: 两个表A\B之间存在外键关系,当插入表A的时候,A的外键B在B表中不存在可以引起这个问题: ...
- EF 实体+ Newtonsoft.Json 输出JSON 时动态忽略属性的解决方法
最近的项目采用的是 ASP.NET mvc 4.0 + entity framework 5.0 ,后台以JSON形式抛出数据是借助于Newtonsoft.Json , 要想忽略的属性前面添加特性 ...
- C#.Net EF实体框架入门视频教程
当前位置: 主页 > 编程开发 > C_VC视频教程 > C#.Net EF实体框架入门视频教程 > kingstone金士顿手机内存卡16G仅65元 1.EF实体框架之增加查 ...
- 利用T4模版生成EF实体
直接上代码,只需要修改EF实体的地址就可以了. <#@ template language="C#" debug="false" hostspecific ...
随机推荐
- 009_原理图中电气互连,Net alias,分页符,总线
009_原理图中电气互连,Net alias,分页符,总线 1.电气互连,就是画线. 2.端口名,适用同一页相连的端口. 3.分页符off page connector,适用于不同页的端口连接. 4. ...
- 2021年5.21NCU第四届校赛
比赛地址:http://222.204.50.106/contest/39 A 树上祖先 链接:http://222.204.50.106/contest/39/problem/A B 莎士比亚 链接 ...
- SAP集成技术(十三)SAP Cloud Integration
异构应用环境给IT带来了各种问题.在这种情况下,混合集成环境尤其受到影响.同时,对于建立在混合IT环境上的数字化转型项目,数据集成和跨系统访问已经开始发挥核心作用.为了满足不断增长的需求,SAP Bu ...
- Ubuntu的apt-get/yarm/wget命令详解
目录 一.查看本机是否安装软件 二.apt-get 三.yum 四.wget apt-cache和apt-get是apt包的管理工具, 他们根据/etc/apt/sources.list里的软件源地址 ...
- SpringBoot模拟插入1000000万条数据
一.数据库表准备 CREATE TABLE `student` ( `id` bigint NOT NULL COMMENT '用户id', `name` varchar(50) COLLATE ut ...
- 【活动访谈】发力数字基座 推动物联创新—航天科技控股集团AIRIOT4.0平台发布会活动专访
近日,由航天科技控股集团股份有限公司主办的"数字基座 智慧物联-AIRIOT4.0平台发布会"在北京圆满落幕.航天三院科技委总工程师王连宝应邀出席本次会议并接受媒体采访,共同参与访 ...
- gpu机器没有开启ipv6
参考: https://blog.csdn.net/asdfaa/article/details/137884414 检查系统是否支持 IPv6,查看被禁用了 在启用 IPv6 之前,首先要确保您的系 ...
- kubernetes之Secret和Configmap
创建和查询Secret literal 播报 编辑 讨论 上传视频literal是一个英语单词,形容词,意思是文字的:逐字的:无夸张的. [1] 通过--from-literal创建以及查看 [mac ...
- iis worker process w3wp 进程 占用率100%
今天电脑特别的卡,我没当回事,但是实在是卡得不行了,我打开任务管理器,发现 iis worker process 进程已经快100%了,我之前在iis上发布了一个webservice,我就把这个网站给 ...
- 部署Zabbix
https://blog.csdn.net/qq_57414752/article/details/125819822