实体框架核心-无法跟踪实体类型的实例,因为已经跟踪了另一个具有键值的实例

时间:2020-01-29 21:45:16

标签: c# asp.net-core entity-framework-core

尝试在数据库上下文中建立多对多关系,在播种数据时遇到以下错误:

无法跟踪实体类型“文件夹”的实例,因为 键值为'{Id:folder001}'的另一个实例是 已经被追踪。附加现有实体时,请确保 仅附有具有给定键值的一个实体实例。

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);
    builder.Entity<UserFolder>(userFolder =>
    {
        userFolder.HasKey(ud => new { ud.UserId, ud.FolderId });

        userFolder.HasOne(uf => uf.Folder)
            .WithMany(f => f.Users)
            .HasForeignKey(uf => uf.FolderId)
            .IsRequired();

        userFolder.HasOne(uf => uf.User)
            .WithMany(f => f.Folders)
            .HasForeignKey(uf => uf.UserId)
            .IsRequired();
    });
}

_

public class Folder
{

    public string Id { get; set; }
    public string FolderName { get; set; }

    public virtual ICollection<UserFolder> Users { get; set; }
}

_

public class User
{

    public string Id { get; set; }
    public string UserName { get; set; }

    public virtual ICollection<UserFolder> Folders { get; set; }
}

_

public class UserFolder
{
    public string UserId { get; set; }
    public virtual User User { get; set; }

    public string FolderId { get; set; }
    public virtual Folder Folder { get; set; }

}

搜索此错误的结果主要是回答有关需要使用Scoped而不是Singleton来注册服务的答案,但是在这里,我以这种方式配置服务:services.AddDbContext<DataContext>,也有一些答案说需要这样做添加.AsNoTracking()

那我该如何摆脱这个错误?

1 个答案:

答案 0 :(得分:1)

您实际上尚未发布与错误相关的代码,但是我可以解释错误的原因。

Entity Framework有一个changetracker,这意味着db上下文具有一种它所了解的实体的“秘密字典”,它像缓存一样用于减少数据库调用并跟踪您的工作和活动这些实体没有变化。这些实体使用其主键存储并在变更跟踪器中引用。

实体框架只能存储给定类型和给定PK值的一个对象。它不能跟踪具有相同值的两个不同的对象。

认为它就像一本字典,对于给定的键值只能存储一个值。字典很乐意用另一个值覆盖一个值,但是EF却拒绝这样做,因为这很可能导致意外的行为并难以解决错误。

以下代码将引发与您遇到的错误相同的错误:

using(var db = new MyContext())
{
    Foo foo1 = new Foo() { Id = 123 };
    Foo foo2 = new Foo() { Id = 123 }; // different object in memory, same PK identifier

    db.Foos.Attach(foo1);
    db.Foos.Attach(foo2); // exception! 
}

我无法告诉您该错误是在代码的何处引起的,因为您没有发布可能发生此错误的代码。