Unable to update child entities using Entity Framework Core 2.1

907 views Asked by At

I am a newbie to EF and am struggling to do what seems like a basic update. I have read numerous posts on similar issues. From what I gather this is not currently easy to do in EF Core 2.1 and requires a complex workaround. Is this actually possible or should I simply update the child entities directly and individually?

Note: if I just update the exampleA and do not include the child entities it works fine.

I get the following error:

An unhandled exception occurred while processing the request. InvalidOperationException: The instance of entity type 'ExampleB' cannot be tracked because another instance with the same key value for {'Id'} 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. Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap.ThrowIdentityConflict(InternalEntityEntry entry)

Stack Query Cookies Headers InvalidOperationException: The instance of entity type 'ExampleB' cannot be tracked because another instance with the same key value for {'Id'} 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.

I have created a basic example of the issue. First stackoverflow post also so let me know if more info is required.

Any help would be greatly appreciated.

WebAPI controller:

[HttpPut]
    public async Task<ActionResult> UpdateExample(ExampleADto exampleADto)
    {
        var exampleAFromDB = await _context.ExampleA.Include(x => x.ExampleBs).Where(x => x.Id == exampleADto.Id).SingleOrDefaultAsync();

        if (exampleAFromDB == null)
            return NotFound();

        _mapper.Map(exampleADto, exampleAFromDB);

        if (await _context.SaveChangesAsync() > 0)
            return Ok(exampleAFromDB);

        throw new Exception("Failed to update ExampleA"); 
    }

exampleA in DB before update:

{
    "id": 1,
    "description": "Example A1",
    "exampleBDtos": [
        {
            "id": 1,
            "description": "B1",
            "someNumber": 1
        },
        {
            "id": 2,
            "description": "B2",
            "someNumber": 2
        },
        {
            "id": 3,
            "description": "B3",
            "someNumber": 3
        }
    ]
}

exampleADto:

{
    "id": 1,
    "description": "Example A1 Updated",
    "exampleBDtos": [
        {
            "id": 1,
            "description": "B1 Updated",
            "someNumber": 1
        },
        {
            "id": 2,
            "description": "B2",
            "someNumber": 2
        },
        {
            "id": 3,
            "description": "B3",
            "someNumber": 3
        }
    ]
}

exampleA after Automapping:

{
"id": 1,
"description": "Example A1 Updated",
"exampleBDtos": [
    {
        "id": 1,
        "description": "B1 Updated",
        "someNumber": 1
    },
    {
        "id": 2,
        "description": "B2",
        "someNumber": 2
    },
    {
        "id": 3,
        "description": "B3",
        "someNumber": 3
    }
]

}

exampleA in DB after failed update (no change):

{
    "id": 1,
    "description": "Example A1",
    "exampleBDtos": [
        {
            "id": 1,
            "description": "B1",
            "someNumber": 1
        },
        {
            "id": 2,
            "description": "B2",
            "someNumber": 2
        },
        {
            "id": 3,
            "description": "B3",
            "someNumber": 3
        }
    ]
}

AutoMapper Profiles:

public AutoMapperProfiles()
    {
        CreateMap<ExampleA, ExampleADto>()
            .ForMember(x => x.ExampleBDtos, opt => opt.MapFrom(x => x.ExampleBs));
        CreateMap<ExampleB, ExampleBDto>();

        CreateMap<ExampleADto, ExampleA>()
            .ForMember(x => x.ExampleBs, opt => opt.MapFrom(x => x.ExampleBDtos));
        CreateMap<ExampleBDto, ExampleB>();
    }

Update 1: Added object after automapping

Update 2: It seems Automapper does not map collections without dropping and recreating the items which messes with EF.

AutoMapper.Collection https://www.nuget.org/packages/AutoMapper.Collection/ does not currently work with EF Core so I have simply created a function myself to manually map and update the collection.

0

There are 0 answers