EF Core中Key屬性相同的實體只能被跟蹤(track)一次

weixin_34321977發表於2018-10-16

在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();
        }
    }
}

結果如下:

 

相關文章