EF Core中Key属性相同的实体只能被跟踪(track)一次
在EF Core的DbContext中,我们可以通过DbContext或DbSet的Attach方法,来让DbContext上下文来跟踪(track)一个实体对象,假设现在我们有User实体对象,其UserCode为Key属性:
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace EFCoreDB.Entities { public partial class User { public User() { UserRole = new HashSet<UserRole>(); } public int Id { get; set; } [Key] public string UserCode { get; set; } public string Username { get; set; } public string Password { get; set; } public string MailAddress { get; set; } public string FirstName { get; set; } public string MiddleName { get; set; } public string LastName { get; set; } public string CompanyCode { get; set; } public DateTime? CreateTime { get; set; } public int? DataStatus { get; set; } public ICollection<UserRole> UserRole { get; set; } } }
现在我们使用DbSet的Attach方法将两个UserCode都为"User001"的User实体Attach到一个DbContext:
using EFCoreDB.Entities; using System; namespace EFCoreDB { class Program { static void Main(string[] args) { using (FinanceDigitalToolContext dbContext = new FinanceDigitalToolContext()) { User user = new User() { UserCode = "User001", Username = "Tom" }; dbContext.User.Attach(user); user = new User() { UserCode = "User001", Username = "Jim" }; dbContext.User.Attach(user); dbContext.SaveChanges(); } Console.WriteLine("Press key to quit...."); Console.ReadLine(); } } }
运行结果如下:
结果在Attach第二个User实体的时候代码抛出了异常,异常信息如下:
The instance of entity type 'User' cannot be tracked because another instance with the same key value for {'UserCode'} 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.
异常信息显示,当前DbContext中已经有一个相同UserCode值的实体被跟踪了,所以Attach第二个User实体的时候失败了。
同样如果我们改为用DbContext的Attach方法来添加第二个User实体也会失败:
using EFCoreDB.Entities; using System; namespace EFCoreDB { class Program { static void Main(string[] args) { using (FinanceDigitalToolContext dbContext = new FinanceDigitalToolContext()) { User user = new User() { UserCode = "User001", Username = "Tom" }; dbContext.User.Attach(user); user = new User() { UserCode = "User001", Username = "Jim" }; dbContext.Attach(user); dbContext.SaveChanges(); } Console.WriteLine("Press key to quit...."); Console.ReadLine(); } } }
但是如果现在我们Attach一个User实体的两个引用是不会报错的,如下所示:
using EFCoreDB.Entities; using System; namespace EFCoreDB { class Program { static void Main(string[] args) { using (FinanceDigitalToolContext dbContext = new FinanceDigitalToolContext()) { User user1 = new User() { UserCode = "User001", Username = "Tom" };//实体User的第一个引用user1 dbContext.User.Attach(user1);//Attach user1 User user2 = user1;//实体User的第二个引用user2,user1和user2实际上指向相同的User实体对象 dbContext.User.Attach(user2);//Attach user2,不会报错 dbContext.SaveChanges(); } Console.WriteLine("Press key to quit...."); Console.ReadLine(); } } }
结果如下,没有报错:
这说明当我们用Attach方法将一个User实体添加到EF Core的DbContext中进行跟踪时,DbContext会判断当前添加的实体是否和DbContext.ChangeTracker.Entries中被跟踪的所有实体是同一个对象,如果是同一个对象,那么其实只是把DbContext.ChangeTracker.Entries中所跟踪实体的EntityState更改为Unchanged,我们可以用下面代码来看看User实体两次Attach后的EntityState值:
using EFCoreDB.Entities; using Microsoft.EntityFrameworkCore; using System; namespace EFCoreDB { class Program { static void Main(string[] args) { using (FinanceDigitalToolContext dbContext = new FinanceDigitalToolContext()) { User user1 = new User() { UserCode = "User001", Username = "Tom" };//实体User的第一个引用user1 dbContext.User.Attach(user1);//Attach user1 dbContext.Entry(user1).State = EntityState.Added;//修改user1实体的EntityState为Added Console.WriteLine($"user1的EntityState为:{dbContext.Entry(user1).State.ToString()}");//显示user1实体的EntityState User user2 = user1;//实体User的第二个引用user2,user1和user2实际上指向相同的User实体对象 dbContext.User.Attach(user2);//Attach user2,不会报错 Console.WriteLine($"user1的EntityState为:{dbContext.Entry(user1).State.ToString()}");//显示user1实体的EntityState,Attach user2后,user1实体的EntityState变为Unchanged dbContext.SaveChanges(); } Console.WriteLine("Press key to quit...."); Console.ReadLine(); } } }
结果如下:
分类:
EF Core
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
2017-10-16 用C#通过反射实现动态调用WebService 告别Web引用(转载)
2009-10-16 ASP.NET 应用程序中实现windows账号登录