无法跟踪实体类型的实例,因为跟踪了具有相同键值的另一个实例

Ali*_*aza 2 .net entity-framework .net-core asp.net-core-2.0

我在asp.net core 2.0中使用通用存储库模式,它无法处理存储库对象,当我要更新条目时,它已成功更新一次,但是当我尝试更新更多时,它会抛出以下异常:

无法跟踪实体类型"公司"的实例,因为已经跟踪了另一个具有{'ID'}相同键值的实例.附加现有实体时,请确保仅附加具有给定键值的一个实体实例.考虑使用'DbContextOptionsBuilder.EnableSensitiveDataLogging'来查看冲突的键值.

public ActionResult Edit(Int64 id, Company collection)
{
    try
    {
        // TODO: Add update logic here
        interfaceobj.updateModel(collection);
        interfaceobj.Save();
        return RedirectToAction(nameof(Index));
    }
    catch(Exception ex)
    {
        throw ex;
    }
}
Run Code Online (Sandbox Code Playgroud)

Tyr*_*ing 22

在 Dotnet 6 中,您只需将其添加到 program.cs 文件中的 dbcontext 选项中即可:

builder.Services.AddDbContext<AppDbContext>(options =>
{
    options.UseSqlServer(connectionString);
    options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
});
Run Code Online (Sandbox Code Playgroud)

  • 你可以这样做很长时间。但这不是一个好主意。通常你想要跟踪。 (4认同)

小智 20

这会帮助你!

AsNoTracking()
Run Code Online (Sandbox Code Playgroud)

默认情况下,EntityFramework 会跟踪所查询实体的更改,并在您对上下文调用 SaveChanges() 时保存更改。

如果它已经在跟踪 product:42,并且您添加或附加了它没有看到的具有 Id:42 的产品,则对于哪个代表真实来源存在歧义。

AsNoTracking 生成的对象未添加到更改跟踪机制中,如果您不使用更改跟踪功能,则应使用该机制,因为它速度更快并使您的意图更加明确。

大多数情况下,这是一个范围问题。您不希望缓存中有什么东西,但令人惊讶的是,有 - 通常是因为某些全局变量或未处理的上下文 - 两者都很常见。当您发现有问题的 DbContext 时,请将其包装在“使用”中或添加 AsNoTracking。

.AsNoTracking() 有什么区别?

  • 减一——愿意详细说明吗?一条链接?你怎么称呼“AsNoTracking()”?(我并不是真的在问这些问题,我只是指出你的答案应该提供哪些细节)。 (5认同)
  • 如果您想要操作实体实例并使用 SaveChanges() 将这些更改保存到数据库,则不应禁用更改跟踪。https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontextoptionsbuilder.usequerytrackingbehavior?view=efcore-3.1 (2认同)

Nic*_*ith 16

我遇到了这个问题,但我的 DbContext 已经注册为 Scoped。我的问题是正在嵌套导航属性中跟踪属性。清空该导航属性(保留外键)为我解决了这个问题。


Pri*_*wen 13

正如 Rudi Visser所建议的那样,在跨多个数据上下文使用一个实体时,您可能会遇到此问题。就我而言,情况与此类似:

User user = await DataContext.Users.SingleOrDefaultAsync(u => u.Id == "SomeId");

// Switch to UserManager *_*
var res = await UserManager.ResetPasswordAsync(user, token, password); // Exception thrown
Run Code Online (Sandbox Code Playgroud)

一个简单的解决方案是尝试坚持使用 one DataContext,因为UserManager似乎在后台进行了一些附加和分离(也许)。例如:

User user = await UserManager.FindByIdAsync("SomeId");
var res = await UserManager.ResetPasswordAsync(user, token, password);
Run Code Online (Sandbox Code Playgroud)


Rud*_*ser 12

您的数据库上下文由多个请求共享,这意味着您正在编辑的实体已被跟踪.

这可能是因为您的存储库服务是Singleton而不是Scoped,因此您的数据库上下文在被拔出时被跟踪的实体重用,然后返回到DB Context的同一实例.

你应该做的是拥有一个Scoped Repository,这意味着将为每个请求创建一个新实例.同样,您还将拥有每请求数据库上下文.

当您注册您的服务时,它将使用 services.AddSingleton<..>

services.AddScoped<..>将其更改为,当您将其注入控制器时,您将获得每个请求的新实例,并且您的更新应该可以正常工作.

  • @JonathanPeel,就我而言,据我所知,这是一个 EF bug...所以为了解决这个问题,首先我做了一些更改,以便看到像这里这样的查询 /sf/ask/98900441/ ,之后我看到 EF 做了一些非常奇怪的事情,不知何故它试图放入已经存在的数据,我注意到它总是有问题的相同属性,最后我发现 EF 不知何故丢失了,因为我在许多类上都有属性字符串“name”,所以我只需将此属性更改为字符串“foo”,它就可以工作并且再也不会崩溃。 (3认同)
  • 实际上我已经在使用 Scoped 了,我已经解决了这个问题,并且似乎是一个 EF bug... (2认同)

小智 6

您需要独立条目-

Entry<CustomerBaseInfo>(data).State = EntityState.Detached;
Run Code Online (Sandbox Code Playgroud)