【问题标题】:How to configure Automapper to initialize every member in destination?如何配置 Automapper 以初始化目标中的每个成员?
【发布时间】:2020-07-06 11:41:35
【问题描述】:

如果成员在源中不存在但在目标中存在,我们需要初始化目标成员。目前我们得到空值,除非我们手动初始化 15 个成员中的每一个:

List<string> Property = new List<string>();

这是我们的服务

    public MappingService()
    {
        var config = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<Car, Bike>();
        });

        _mapper = config.CreateMapper();
    }

    public Bike MapToBike(Car car)
    {
        return _mapper.Map<Car, Bike >(car);
    }

【问题讨论】:

  • 不太清楚你想要实现什么。也许 .ForMember 或 .AfterMap 会帮助你
  • 您的问题没有包含足够的信息,任何人都无法提供任何帮助。
  • @Neistow 我们希望每个目标成员的值都来自源或至少初始化为其默认值,而不是 null。因此,对于 List 示例,使用 0 个元素初始化 List,而不是 null List。
  • @Sнаđошƒаӽ 我添加了更多信息,如果需要更多信息来帮助解决这个问题,请告诉我。
  • List 的默认值为 null,不是空列表。您是否希望仅以这种方式处理列表属性,即使用空列表进行初始化?

标签: c# automapper


【解决方案1】:

您可能不应该依赖 AutoMapper 来初始化您的集合。毕竟,将 null 作为属性值是一个有效的情况。 也就是说,虽然没有标准的方法可以做到这一点,但您可以设计一些可能有效的方法。例如,如前所述,您可以使用.AfterMap() 将所有空集合填充为空集合。这是一个扩展方法:

/// <summary>
/// Populates all properties of standard collection types with empty collections
/// </summary>
public static IMappingExpression<TSrc, TDest> InitializeNullCollectionProps<TSrc, TDest>(this IMappingExpression<TSrc, TDest> map)
{
    return map.AfterMap((src, dest) =>
    {
        var destType = typeof(TDest);

        // find all applicable properties
        var collectionProps = destType.GetProperties(BindingFlags.Public)
            .Where(propInfo =>
            {
                if (!propInfo.CanRead)
                {
                    return false;
                }

                // check if there is public setter 
                if (propInfo.GetSetMethod() == null)
                {
                    return false;
                }
                
                var propertyType = propInfo.PropertyType;
                // it can be Array, List, HashSet, ObservableCollection, etc
                var isCollection = !propertyType.IsAssignableFrom(typeof(IList));
                if (!isCollection)
                {
                    return false;
                }

                var haveParameterlessCtor =
                    propertyType.GetConstructors().Any(ctr => !ctr.GetParameters().Any());
                if (!haveParameterlessCtor)
                {
                    return false;
                }

                return true;
            })
            .ToList();
        
        // assign empty collections to applicable properties
        foreach (var propInfo in collectionProps)
        {
            var value = propInfo.GetValue(dest);
            if (value == null)
            {
                propInfo.SetValue(dest, Activator.CreateInstance(propInfo.GetType()));
            }
        }
    });
}

它的用法就是:

Mapper.CreateMap<Src,Dest>()
   // some mappings
   .InitializeNullCollectionProps()
   // some mappings

但我不认为有一种方法可以准确地确定该成员是否已经从源映射到 null,所以无论如何它都会有空的集合值。

【讨论】:

  • OP 提到“如果该成员在 src 中不存在,但在目标中存在..”,但您没有解决这个问题。一旦你这样做了,你在最后一段中的担忧将不再是一个问题。对不对?
  • @Sнаđошƒаӽ 是的,正如我提到的,没有简单的方法可以知道属性是否被映射。对于具有相同名称的自动映射成员来说,这很简单,但是有很多方法可以不同地映射目标值(值解析器、不同命名的 src prop 等)。只检查 src 属性名称是不安全的。
  • 为什么不呢?检查属性是否存在于 dest 而不是 src 似乎是确定该属性不会被自动映射的有效方法。
  • @Sнаđошƒаӽ 考虑映射:.ForMember(dest =&gt; dest.Foo, o =&gt; o.MapFrom(src =&gt; src.Bar))。如果src.Bar 为空,是否应该初始化dest.Bar?我假设最终 OP 只希望激活所有集合而不是准确的空集合映射。否则,找到真正的值源以确保它在 dest 上不应该为 null 将是非常重要的。坦率地说,我认为这段代码不会很有用。
猜你喜欢
  • 1970-01-01
  • 2013-09-22
  • 2017-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-14
  • 2019-10-23
相关资源
最近更新 更多