【问题标题】:Using AutoMapper to update property on owned entity doesn't set it as Modified使用 AutoMapper 更新拥有实体的属性不会将其设置为已修改
【发布时间】:2019-12-19 13:29:22
【问题描述】:

尝试将此作为问题发布在 AutoMapper 的 GitHub 上,但它已关闭并指向此处。

在 EF Core 3.1 中,使用 AutoMapper 更新具有自有类型的实体不会触发 EF Core 的更改检测。

EF Core 2.2 中不存在此问题。

来源/目的地类型

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

映射配置

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

CreateMap<SomeOwnedType, SomeOwnedType>();

版本:EF Core 3.1 上的 9.0.0

AutoMapper 集合 5.0.0

AutoMapper 扩展 Microsoft DependencyInjection 7.0.0

预期行为

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

实际行为

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

重现步骤

如上Expected behaviour

顺便说一句,手动修改字段会将预期的实体标记为已修改:

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

entity.Something.SomeString = "abcde";

context.Entities.Update(entity);

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

奇怪的是,这个问题不会出现在干净启动的 .NET Core 3.1 项目中,而只会出现在之前在 .NET Core 2.2 上的升级项目中。

【问题讨论】:

  • 在 EF Core 3 中,拥有的类型现在可以为 null,如果它们没有任何必填字段。自动映射器是否已更新以应对这种语义变化?
  • 您是在询问 AutoMapper 本身是否已更新?据我所知,应该没有必要,因为检查执行计划表明它将处理null 拥有的实体。如果您的意思是我应该更新我的 AutoMapper 配置,您能看出我遗漏了什么?
  • SomeEntity的PK在哪里?它是否包含在SomeEntityForm 中?请更新帖子中的代码,以便重现问题。
  • 更新了主键字段。表单中没有Id 不会影响映射。
  • 在我的测试中它确实很重要。另外,关于var entity = …; - 您是否从上下文中检索实体并使用跟踪查询,即实体是否附加到上下文?请包含完整的可重现示例,使用 EF Core,每个细节都很重要。

标签: c# entity-framework-core automapper


【解决方案1】:

我对这个问题的“解决方案”是:

  • 使用迁移在同一类型的父级上创建另一个拥有的属性
  • 使用另一个迁移将数据从原始列复制到新列中
  • 使用另一个迁移来删除旧列
  • 使用另一个迁移将新列重命名为旧列

这很糟糕,远非理想,但它有效,我没有其他问题。

编辑:通过进一步的修改,我认为它与可为空的拥有实体以及当一个新实例添加到父级时跟踪如何工作有关。

【讨论】:

    猜你喜欢
    • 2018-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-25
    • 1970-01-01
    相关资源
    最近更新 更多