DbContext.Attach 在 Ef Core 3.0 上的行为与 2.2 不同,失败并显示"The instance of entity type cannot be tracked"



我正在将使用EF Core 2.2的ASP.NET Core 2.2应用程序迁移到ASP.NET Core 3.0和EF Core 3.0。

在一个特定的方法中,后端从前端接收DTO,并使用Automapper将其转换为等效的实体类型,将其附加到DbContext并保存。这在EF Core 2.2上运行良好,但在EF Core 3.0上出现以下消息时失败:

The instance of entity type 'ServiceProvider' cannot be tracked because another
instance with the same key value for {'Id'} is already being tracked

这是有问题的代码:

var sqresponse = _mapper.Map<SurveyQuestionResponse>(sqresponseDTO);
_context.Attach(sqresponse); // <=== Throws exception
_context.SaveChanges();

以下是具有相关类的实体:

public class SurveyQuestionResponse
{
public int? Id { get; set; }
public List<Answer> Answers { get; set; } = new List<Answer>();
}
public class Answer
{
public int? Id { get; set; }
public int SurveyQuestionResponseId { get; set; }
public ServiceProvider ServiceProvider { get; set; }
}

从前端传递给该方法的数据有两个Answer,它们都包含一个带有Id = 56ServiceProvider

{
answers: [
{
// some more properties here
serviceProviderId: 56,
serviceProvider: { id: 56, name: "Foo" },
},
{
// some more properties here
serviceProviderId: 56,
serviceProvider: { id: 56, name: "Foo" },
}
]
}

这两个ServiceProvider实体意味着存在于数据库中;在这种情况下,我需要与具有{Id: 56}的现有ServiceProvider实体关联的两个Answer这似乎是EntityFramework Core 3.0的一个问题,但对于EF Core 2.2来说完全可以。

我怀疑这与这个突破性的更改有关:DetectChanges尊重存储生成的键值,但我认为所提出的缓解措施可以解决这个特定的问题,但在尝试插入新的ServiceProvider实体时会创建另一个问题。我说得对吗?

如何将其与EF Core 3.0配合使用?

这在EFCore 2.2中也不应该起作用,因此您的使用/测试用例必须有足够的不同。

提交的JSON将被反序列化为实体对象。相关属性serviceProvider将被反序列化/具体化每个应答实体对象的实体对象-这意味着,在您的示例中,两个应答对象将引用两个具有相同ID的不同ServiceProvider对象。当它们的引用answer对象被附加时,EFCore也将尝试附加它们的引用的ServiceProvider对象,但尝试附加第二个答案对象将抛出您收到的错误,因为上下文已经在跟踪具有相同ID的ServiceProvider实体,而该实体不是同一对象。

在尝试将实体添加到上下文之前,您需要检查它是否已经被跟踪,这意味着您不能盲目地将DTO附加到上下文。

在通过DTO附加answer对象之前,首先检查上下文是否已经跟踪到具有相同ID的该类型的实体对象,并将该对象分配给serviceProvider属性:

var attachedServiceProvider = dbContext.ServiceProviders
.Local
.FirstOrDefault(sp => sp.Id = answerObject.serviceProvider.Id);
if( null == attachedServiceProvider )
{
dbContext.Attach(serviceProvider);
attachedServiceProvider = serviceProvider;
}
answerObject.serviceProvider = attachedServiceProvider;

我最终解决了这个问题,确保只填写了answer.ServiceProviderId,并且在附加时answer.ServiceProvidernull

传递到后端的JSON现在看起来是这样的:

{
answers: [
{
// some more properties here
serviceProviderId: 56,
serviceProvider: null,
},
{
// some more properties here
serviceProviderId: 56,
serviceProvider: null,
}
]
}

这样,对_context.Attach(...)_context.SaveChanges()的调用就可以毫无问题地工作。

相关内容

最新更新