zoukankan      html  css  js  c++  java
  • 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();
            }
        }
    }

    结果如下:

  • 相关阅读:
    hdu1251 && hud 1247 (字典树)
    linux系统调用kernel code
    Android init.rc 服务启动不成功
    android init执行service的脚本code位置
    printk %pS %pF含义
    进程页表图示
    linux内核之进程的基本概念(进程,进程组,会话关系)
    What is the purpose of the controlling terminal?
    android init.rc中service console option的含义、作用
    启动和停止init.rc中的服务
  • 原文地址:https://www.cnblogs.com/OpenCoder/p/9799823.html
Copyright © 2011-2022 走看看