记录一次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实体跟踪错误的更多相关文章

  1. EF实体框架之CodeFirst一

    对于SQL Server.MySql.Oracle等这些传统的数据库,基本都是关系型数据库,都是体现实体与实体之间的联系,在以前开发时,可能先根据需求设计数据库,然后在写Model和业务逻辑,对于Mo ...

  2. EF实体框架之CodeFirst四

    在EF实体框架之CodeFirst二中也提到数据库里面一般包括表.列.约束.主外键.级联操作.实体关系(E-R图).存储过程.视图.锁.事务.数据库结构更新等.前面几篇博客把表.存储过程.视图这些算是 ...

  3. 实习笔记-3:ef实体操作错误篇

    学习笔记 1.json序列化ef实体是报错:“序列化类型为“System.Data.Entity.DynamicProxies.XXXX.... 对象时检测到循环引用.” 公司里用ef来生成实体.但是 ...

  4. 匿名类型 使用泛型T linq返回dynamic类型的匿名实体 如何把匿名类型.GetType()返回的对象传进泛型里面 EF实体查询出的数据List<T>转DataTable出现【DataSet 不支持 System.Nullable<>】的问题

    [100分]紧急求助:LinQ下使用IQueryable<T>如何将返回类型<T>使用匿名类型 问题描述如下:我有一个方法如下:public IQueryable Dissen ...

  5. 【MVC 1】MVC+EF实体框架—原理解析

    导读:在之前,我们学过了三层框架,即:UI.BLL.DAL.我们将页面显示.逻辑处理和数据访问进行分层,避免了一层.两层的混乱.而后,我们又在经典三层的基础上,应用设计模式:外观.抽象工厂+反射,使得 ...

  6. BIM工程信息管理系统-EF实体框架数据操作基类

    EF实体框架数据操作基类主要是规范增.改.查.分页.Lambda表达式条件处理,以及异步操作等特性,这样能够尽可能的符合基类这个特殊类的定义,实现功能接口的最大化重用和统一. 1.程序代码 /// & ...

  7. EF非常见错误:EXECUTE 后的事务计数指示 BEGIN 和 COMMIT 语句的数目不匹配

    EF非常见错误:EXECUTE 后的事务计数指示 BEGIN 和 COMMIT 语句的数目不匹配 问题原因: 两个表A\B之间存在外键关系,当插入表A的时候,A的外键B在B表中不存在可以引起这个问题: ...

  8. EF 实体+ Newtonsoft.Json 输出JSON 时动态忽略属性的解决方法

    最近的项目采用的是 ASP.NET mvc 4.0 + entity framework 5.0 ,后台以JSON形式抛出数据是借助于Newtonsoft.Json ,   要想忽略的属性前面添加特性 ...

  9. C#.Net EF实体框架入门视频教程

    当前位置: 主页 > 编程开发 > C_VC视频教程 > C#.Net EF实体框架入门视频教程 > kingstone金士顿手机内存卡16G仅65元 1.EF实体框架之增加查 ...

  10. 利用T4模版生成EF实体

    直接上代码,只需要修改EF实体的地址就可以了. <#@ template language="C#" debug="false" hostspecific ...

随机推荐

  1. Docker 之 Dockerfile

    Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明. 一.官方镜像 学习docker镜像的制作之前,先去看看官方的镜像是怎么制作的,查看官方dockerf ...

  2. CF522D Closest Equals 离线扫描 + 线段树

    CF522D Closest Equals 题意:m 个询问,求 [l,r] 内相同元素的最小距离. 离线询问,按右端点排序. 对于每一个 a[i],如果 last[a[i]] 存在,将线段树 las ...

  3. Radius 现在是云原生计算基金会(CNCF)的沙箱项目

    在数字化时代,云原生计算技术逐渐成为企业转型的关键.2024-04-25,备受瞩目的开源项目 Radius 已正式加入云原生计算基金会(CNCF)的沙箱项目![Sandbox] Radius · Is ...

  4. Golang重复Rails Devise gem密码加密

    https://github.com/haimait/go-devise-encryptor package main import ( "fmt" //devisecrypto ...

  5. UE4 InstancedStaticMesh使用

    在绘制大批量近似模型时,Unity有GPU Instancing(https://www.cnblogs.com/hont/p/7143626.html),而UE中有 HISM和ISM(Instanc ...

  6. 使用c#强大的表达式树实现对象的深克隆之解决循环引用的问题

    在上一期博客里,我们提到使用使用c#强大的表达式树实现对象的深克隆,文章地址:https://www.cnblogs.com/gmmy/p/18186750.但是文章里没有解决如何实现循环引用的问题. ...

  7. 添加身份认证和鉴权方案-使用jwtbearer

    HTTP身份认证框架 RFC 7235 定义了一个 HTTP 身份验证框架,服务器可以用来质询(challenge)客户端的请求,客户端则可以提供身份验证凭据. 服务端开启了身份认证后.如果客户端访问 ...

  8. 你可能不知道的秘密——String s=new String(“abc”)到底创建了几个对象?

    String s=new String("abc")创建了几个对象? ​   在探讨上述问题结论之前,我们需要了解常量池这个概念. 常量池 ​   由于字符串在Java中被大量使用 ...

  9. AIAGC导航(aiagc.com): 最全的AI工具导航网站

    AIAGC导航是一个专注于AI人工智能工具网站推荐的导航网站,可以帮助大家发现最新.最好用.最有趣的AI绘画.AI智能写作助手.AI聊天机器人.AI配音.AI音乐.AI换脸等各种AI工具应用软件,让A ...

  10. NET框架下如何使用PaddleOCRSharp

    打开VSIDE,新建Windows窗体应用(.NET Framework)类型的项目,选择一个.NET框架,如.NET Framework 4.0,右键点击项目,选择属性>生成,目标平台设置成X ...