如何使用Automapper将更改映射到现有集合?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用Automapper将更改映射到现有集合?相关的知识,希望对你有一定的参考价值。

我有一些相当简单的代码,可以将viewmodel中集合的用户更改应用于模型中的集合。

public void Apply(ViewModelListItem source, ICollection<ModelListItem> dest)
{
    //user added and removed an item before saving, do nothing
    if (source.Insert && source.Delete) return;

    //user added an item
    if (source.Insert)
    {
        dest.Add(Mapper.Map<T>(source));
    }

    //user deleted an item
    else if (source.Delete)
    {
        //Using custom Equals implementation that compares PK
        dest.Remove(dest.FirstOrDefault(destItem => source.Equals(destItem)));
    }

    //user modified or did not alter an item
    else
    {
        //Using custom Equals implementation that compares PK
        Mapper.Map(source, dest.FirstOrDefault(destItem => source.Equals(destItem)));
    }
}

...

foreach (var item in MyViewModel.MyCollection)
{
    Apply(item, MyModel.MyCollection);
}

我在代码中的多个位置使用此模式,因此我一直在寻找以通用方式重用代码的方法。 Automapper是否可以简单地将引用传递给源/目标,让我在其上运行自己的设置逻辑?如果没有,有没有其他方法可以使这个代码通用,所以我不必为每个新的viewmodel写它?

答案

我的解决方案

Viewmodel的基类:

public abstract class ListItemViewModel<T>
{
    public bool Insert { get; set; }
    public bool Delete { get; set; }

    public abstract bool Equals(T model);

    public virtual void OnRemove(T model) { }
    public virtual void OnAdd(T model) { }
    public virtual void OnEdit(T model) { }
}

辅助功能:

public static class MapConfiguration
{
    public static void MapCollection<T>(IEnumerable<ListItemViewModel<T>> source, ICollection<T> dest)
    {
        foreach (var sourceItem in source)
        {
            //user added and removed an item before saving, do nothing
            if (sourceItem.Insert && sourceItem.Delete) continue;

            //user added an item
            if (sourceItem.Insert)
            {
                var destItem = Mapper.Map<T>(sourceItem);
                sourceItem.OnAdd(destItem);
                dest.Add(destItem);
            }

            //user deleted an item
            else if (sourceItem.Delete)
            {
                //Using custom Equals implementation that compares PK
                var destItem = dest.First(d => sourceItem.Equals(d));
                sourceItem.OnRemove(destItem);
                dest.Remove(destItem);
            }

            //user modified or did not alter an item
            else
            {
                //Using custom Equals implementation that compares PK
                var destItem = dest.First(d => sourceItem.Equals(d));
                sourceItem.OnEdit(destItem);
                Mapper.Map(sourceItem, destItem);
            }
        }
    }
}

实施:

  • 创建一个继承自ListItemViewModel的视图模型类,其中T是模型类型
  • 实现Equals(T model)方法,因此辅助函数可以匹配可枚举中的现有元素
  • 将ListItemViewModel的继承类型的通用可枚举属性添加到主视图模型中

配置automapper时,您现在可以使用以下内容:

.ForMember(m => m.MyList, opt => opt.Ignore())
.AfterMap((vm, m) => MapConfiguration.MapCollection(vm.MyList, m.MyList)) 

此代码将执行以下操作:

  • 如果设置了Insert标志,请将新项添加到集合中
  • 如果设置了Remove标志,则从集合中删除项目
  • 如果两个标志都未设置,则执行自动映射映射
另一答案

看看AutoMapper.Collection。它自动处理集合上的所有CRUD操作:

将根据源集合中集合的通用项类型与目标集合之间的用户定义等效性,从预先存在的集合对象添加/更新/删除项目

以上是关于如何使用Automapper将更改映射到现有集合?的主要内容,如果未能解决你的问题,请参考以下文章

C#AutoMapper 使用手册

如何使用 AutoMapper 将 json 请求 dto 中的 OData 枚举字符串映射到实体枚举属性

如何使用 AutoMapper 将父引用分配给子属性

使用链接地图/传递映射/链式地图的 AutoMapper 地图

如何调试到 AutoMapper 代码?

ASP.NET Core 中的对象映射之 AutoMapper