如何防止 AutoMapper 覆盖目标对象上的现有值?

How to prevent AutoMapper from overwriting existing values on destination object?

我有以下实体和相应的视图模型

public class TopEntity
{
  public int Id { get; set; }

  public ICollection<BottomEntity> Bottoms { get; set; }
}

public class BottomEntity
{
  public int Id { get; set; }

  public int TopId { get; set; }
}

public class TopEntityViewModel
{
  public int Id { get; set; }

  public ICollection<BottomEntityViewModel> Bottoms { get; set; }
}

public class BottomEntityViewModel
{
  public int Id { get; set; }
}

我想将类型 TopEntityViewModel 的实例映射到类型 TopEntity 的实例,而不覆盖 BottomEntity.TopId 上可能已经存在的任何值。

我按如下方式配置了 AutoMapper:

var mapperConfig = new MapperConfiguration(exp =>
{
  exp.CreateMap<TopEntity, TopEntityViewModel>();

  exp.CreateMap<BottomEntity, BottomEntityViewModel>();

  exp.CreateMap<TopEntityViewModel, TopEntity>();

  exp.CreateMap<BottomEntityViewModel, BottomEntity>()
    .ForMember(dest => dest.TopId, opts => opts.Ignore());
});

我添加了 .ForMember(dest => dest.TopId, opts => opts.Ignore()); 以在映射期间忽略 BottomEntity.TopId,认为它应该有助于保留现有值。

我正在像这样映射实例:

mapperConfig.AssertConfigurationIsValid();
            
var mapper = mapperConfig.CreateMapper();

var topEntity = new TopEntity
{
  Id = 45,
  Bottoms = new List<BottomEntity>
  {
    new BottomEntity
    {
      Id = 56,
      TopId = 45
    },
    new BottomEntity
    {
      Id = 57,
      TopId = 45
    }
  }
};

var topEntityVm = new TopEntityViewModel
{
  Id = 45,
  Bottoms = new List<BottomEntityViewModel>
  {
    new BottomEntityViewModel
    {
      Id = 56,
    },
    new BottomEntityViewModel
    {
      Id = 57
    }
  }
};

            
var updatedTopEntity = mapper.Map(topEntityVm, topEntity);

当我访问 updatedTopEntity.Bottoms[<whatever>].TopId 时,它总是设置为 0。 如果我更新 class 上 属性 BottomEntity.TopId 的定义以具有类似 1000 的默认值,映射后我的所有 updatedTopEntity.Bottoms[<whatever>].TopId 都设置为该默认值值(示例中的 1000)。
我检查了从 mapper.Map 返回的引用是否映射了原始引用,它们确实映射了。

如何防止 AutoMapper 删除现有实例上的任何现有值?

我在 .NET Core 5 中使用 AutoMapper 10.1.1。You can try a working example here

AutoMapper 这样说 in the documentation:

When mapping to an existing collection, the destination collection is cleared first. If this is not what you want, take a look at AutoMapper.Collection.

AutoMapper.Collection,根据文档,确实:

Adds ability to map collections to existing collections without re-creating the collection object.
Will Add/Update/Delete items from a preexisting collection object based on user defined equivalency between the collection's generic item type from the source collection and the destination collection.

安装后 AutoMapper.Collection 我更新了 AutoMapper 配置如下:

var mapperConfig = new MapperConfiguration(exp =>
  {
    exp.AddCollectionMappers();
                
    exp.CreateMap<TopEntity, TopEntityViewModel>();

    exp.CreateMap<BottomEntity, BottomEntityViewModel>();

    exp.CreateMap<TopEntityViewModel, TopEntity>();

    exp.CreateMap<BottomEntityViewModel, BottomEntity>()
      .ForMember(dest => dest.TopId, opts => opts.Ignore())
      .EqualityComparison((src, dest) => src.Id == dest.Id);
  });

.EqualityComparison((src, dest) => src.Id == dest.Id); 添加了 AutoMapper 将使用的相等比较,如下所示:

  • 如果 ID 匹配,则 AutoMapper 会将 BottomEntityViewModel 映射到 BottomEntity
  • 如果 BottomEntityViewModel 存在而 BottomEntity 不存在,则 AutoMapper 将从 BottomEntityViewModel 映射的新 BottomEntity 添加到集合中
  • 如果 BottomEntity 存在而 BottomEntityViewModel 不存在,则 AutoMapper 将从集合中移除 BottomEntity

AutoMapper.Collectionis described here
的这种行为 工作示例:https://dotnetfiddle.net/tnaUjY

感谢Lucian Bargaoanu给我指明了正确的方向