ef-core-3.0netcoreapp3.0

DbContext.Attach behaves differently on Ef Core 3.0 than 2.2, fails with "The instance of entity type cannot be tracked"


I am migrating an ASP.NET Core 2.2 application using EF Core 2.2 to ASP.NET Core 3.0 anf EF Core 3.0.

On one particular method, the back-end receives a DTO from the front-end and, using Automapper, converts it to the equivalent entity type, attaches it to the DbContext, and saves. This works perfectly on EF Core 2.2, but fails with the following message on 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

This is the code in question:

var sqresponse = _mapper.Map<SurveyQuestionResponse>(sqresponseDTO);

_context.Attach(sqresponse); // <=== Throws exception
_context.SaveChanges();

And here are the entities with the relevant classes:

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; }
}

The data passed to that method from the front-end has two Answer, both of them containing a ServiceProvider with Id = 56.

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

These two ServiceProvider entities are meant to exist in the database; I need both Answer in this case associated with the existing ServiceProvider entity with {Id: 56} This seems to be a problem for EntityFramework Core 3.0, but was perfectly fine for EF Core 2.2.

I suspect this has to do with this breaking change: DetectChanges honors store-generated key values, but I think the proposed Mitigation would fix this particular problem, but create another one when trying to insert a new ServiceProvider entity. Am I correct?

How can this be made to work with EF Core 3.0?


Solution

  • I ended up solving it by making sure only answer.ServiceProviderId is filled in, and answer.ServiceProvider is null when attaching.

    The JSON being passed to the backend now looks like this, then:

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

    With this, the calls to _context.Attach(...) and _context.SaveChanges() work without problems.