How to prevent Automapper from replacing Entity Framework owned entities?

.net-core asp.net-core-webapi automapper c# entity-framework-core

Question

I have two types. Location and Location has an Address. Address is specified as an owned entity using

class LocationConfiguration : IEntityTypeConfiguration<Location>
{
    public void Configure(EntityTypeBuilder<Location> builder)
    {
        builder.HasKey(location => new { location.SubscriptionId, location.Id });
        builder.OwnsOne(location => location.Address);
    }
}

I am fetching an existing Location entity and mapping updated values using Automapper.

[HttpPut("{subscriptionId}/{locationId}")]
public async Task<IActionResult> SaveLocationAsync(string subscriptionId, long locationId, [FromBody] Location location)
{
    if (location == null || location.Id != locationId || location.SubscriptionId != subscriptionId)
    {
        return BadRequest();
    }
    var dbLocation = await locations.GetLocationAsync(subscriptionId, locationId);
    if (dbLocation == null)
    {
        return NotFound();
    }
    mapper.Map<Location, Location>(location, dbLocation);
    return Ok(await locations.SaveAsync(dbLocation));
}

I am saving by calling context.SaveChangesAsync();

But I am getting the error

InvalidOperationException: The instance of entity type 'Location.Address#Address' cannot be tracked because another instance with the key value 'LocationSubscriptionId:123, LocationId:1' is already being tracked. When replacing owned entities modify the properties without changing the instance or detach the previous owned entity entry first.

I suspect that Automapper is replacing the Address property of the Location rather than navigating down and replacing the properties of the Address individually.

Is there way to make Automapper do a more granular copy of property values?

1
1
1/14/2018 5:19:17 PM

Accepted Answer

You should configure such properties in the owner type mapping configuration with UseDestinationValue:

UseDestinationValue tells AutoMapper not to create a new object for some member, but to use the existing property of the destination object.

Also, in case you are using self mapping as in the sample, make sure to create explicit self mapping for each owned type.

For your sample, the minimal AutoMapper configuration for the desired behavior is as follows:

cfg.CreateMap<Address, Address>();

cfg.CreateMap<Location, Location>()
    .ForMember(dest => dest.Address, opt => opt.UseDestinationValue());
5
1/13/2018 5:19:51 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow