Reputation: 5703
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?
Upvotes: 2
Views: 2383
Reputation: 205579
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());
Upvotes: 8