Using AutoMapper to update property on owned entity doesn't set it as Modified

automapper c# entity-framework-core

Question

Tried posting this as an issue on AutoMapper's GitHub but it was insta-closed and pointed here.

In EF Core 3.1, using AutoMapper to update an entity with an owned type does not trigger EF Core's change detection.

This issue was not present in EF Core 2.2.

Source/destination types

public class SomeOwnedType
{
    public string SomeString { get; set; }
}
public class SomeEntity
{
    [Key]
    public int Id { get; set; }
    public SomeOwnedType Something { get; set; } = new SomeOwnedType();
    public DateTime? UpdatedAt { get; set; }
}
public class SomeEntityForm
{
    public int Id { get; set; }
    public SomeOwnedType Something { get; set; } = new SomeOwnedType();
    public DateTime? UpdatedAt { get; set; }
}
public class MyContext : IdentityDbContext<...>
{
    public DbSet<SomeEntity> Entities { get; set; }
    protected override void OnModelCreating(ModelBuilder builder) =>
        builder.Entity<SomeEntity>().OwnsOne(e => e.Something);
}

Mapping configuration

CreateMap<SomeEntity, SomeEntityForm>()
    .ReverseMap()
    .ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
    .ForMember(dest => dest.Something, opt => opt.UseDestinationValue());

CreateMap<SomeOwnedType, SomeOwnedType>();

Version: 9.0.0 on EF Core 3.1

AutoMapper Collection 5.0.0

AutoMapper Extensions Microsoft DependencyInjection 7.0.0

Expected behavior

public async Task<IActionResult> SomeAction([FromForm] SomeEntityForm form)
{
    var entity = await context.Entities.FirstOrDefaultAsync();
    if (entity == null)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        mapper.Map(form, entity);
        entity.UpdatedAt = DateTime.Now;

        context.Entities.Update(entity);

        var changes = context.ChangeTracker.Entries();
        // entity -> Modified
        // entity.Something -> Modified

        return Ok();
    }

    return BadRequest();
}

Actual behavior

var form = ...; // Something.SomeString = "qwerty"
var entity = ...; // Something.SomeString = "abcde"

mapper.Map(form, entity);
// entity.Something.SomeString = "qwerty"

context.Entities.Update(entity);

var changes = context.ChangeTracker.Entries();
// entity -> Modified
// no entry for entity.Something

Steps to reproduce

As above in Expected behaviour.

Incidentally, manually modifying the field marks the expected entities as modified:

var entity = ...; // Something.SomeString = "qwerty"

entity.Something.SomeString = "abcde";

context.Entities.Update(entity);

var changes = context.ChangeTracker.Entries();
// entity -> Modified
// entity.Something -> Modified

Oddly enough, this issue does not occur in a cleanly spun up .NET Core 3.1 project, but only appears in an upgraded project previously on .NET Core 2.2.

1
0
12/19/2019 1:18:57 AM

Popular Answer

My "solution" to this problem was to:

  • Use a migration to create another owned property on the parent of the same type
  • Use another migration to copy the data from the original columns into the new
  • Use another migration to drop the old columns
  • Use another migration to rename the new columns to the old

It's terrible and far from ideal, but it works and I have no further issues.

EDIT: with further tinkering, I think it has to do with nullable owned entities and how tracking works when a new instance of one is added to the parent.

0
12/19/2019 5:29:22 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