在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(); } } }
結果如下: