AutoMapper:“忽略其余部分”?

245

有没有一种方法可以告诉AutoMapper忽略除了明确定义映射的属性以外的所有属性?

我有一些外部的DTO类,这些类可能会从外部发生变化,我想避免明确指定要忽略每个属性,因为添加新属性时,将尝试将它们映射到我的对象中会导致功能出现问题(引起异常)。


1
通过ValueInjecter(http://valueinjecter.codeplex.com/documentation),您可以创建具有其映射算法的ValueInjections,用于在特定属性之间进行映射,而不关心其余属性。 - Omu
33
如果您使用的是 Automapper 5 及以上版本,请跳至下面查看有关 .ForAllOtherMembers(opts => opts.Ignore()) 的答案。 - Jack Ukleja
2
@Schneider ".ForAllOtherMembers(opts => opts.Ignore())" 和扩展名"IgnoreAllNonExisting"不同,主要区别在于如果您没有显式配置属性,则使用".ForAllOtherMembers(opts => opts.Ignore())"将无法映射任何内容。而使用"IgnoreAllNonExisting"时,即使没有显式配置属性,仍然会将一些具有相同名称的属性映射为值。 - Dragon
是的。对于其他所有成员,ForAllOtherMembers 可以解决问题。IgnoreUnmapped 不会有任何作用,因为未映射的成员会被忽略,只会导致 config-valid-assert 通过。 - N73k
1
值得注意的是,在执行此操作时,您明确隐藏了可能与映射的类相关或重要的更改。为每个属性设置显式映射将在映射的类发生更改时留下一个破损的测试,迫使您对其进行适当评估(假设您有一个执行“AssertConfigurationIsValid()”调用的测试)。因此,我认为“忽略其余部分”是一种反模式。 - Arve Systad
AutoMapper 11的解决方案是https://dev59.com/Z8Tra4cB1Zd3GeqP8XeF#73333328。 - Michael Freidgeim
23个回答

9

您希望如何指定忽略某些成员?是否有惯例、基类或属性可以应用?一旦您开始明确指定所有映射,我不确定您会从AutoMapper中获得什么价值。


Jimmy,您关于明确性的观点是正确的。至于如何以最优雅的方式实现它:基类和属性在这种情况下不起作用,因为目标类并不真正受到我的控制-它们是从XSD数据合同自动生成的,因此每个生成周期后都需要手动编辑此代码。我想解决方案取决于具体情况。也许像温莎城堡提供的流畅接口一样,用于选择要在容器中注册哪些组件的解决方案可能是一个解决方案? - Igor Brejc
啊,现在更有意义了。那是一个有趣的功能,我会在2.1时期研究一下它。 - Jimmy Bogard
2
怎么样只设置一个可配置的值,可以“忽略”所有不存在的字段。 - Ricardo Sanchez
7
这不是对问题的答案。 - user2864740
嗨,Jimmy,你是作者,对吧?我想能够忽略所有不存在的属性作为默认行为(可以通过标志控制)。此外,我遇到了一个奇怪的 AutoMapper 错误,我无法弄清楚。它没有给我任何具体信息。 - Naomi

7

这似乎是一个老问题,但我想为任何像我一样在寻找答案的人发布我的答案。

我使用ConstructUsing和对象初始化器结合ForAllMembers忽略例如:

    Mapper.CreateMap<Source, Target>()
        .ConstructUsing(
            f =>
                new Target
                    {
                        PropVal1 = f.PropVal1,
                        PropObj2 = Map<PropObj2Class>(f.PropObj2),
                        PropVal4 = f.PropVal4
                    })
        .ForAllMembers(a => a.Ignore());

5

默认情况下,AutoMapper使用目标类型来验证成员,但您可以通过使用MemberList.None选项来跳过验证。

var configuration = new MapperConfiguration(cfg =>
  cfg.CreateMap<Source2, Destination2>(MemberList.None);
);

你可以在这里找到参考文档:链接

4
在 dotnet 5 的 WebApi 中,使用 Nuget 包 AutoMapper.Extensions.Microsoft.DependencyInjection,我在 mapper 配置文件中这样写。虽然我不怎么熟悉 AutoMapper ,但它现在似乎能正常处理未映射成员。
在 Startup 文件中:
var mapperConfig = new MapperConfiguration(mc => mc.AddProfile(new AutoMapperProfile()));
    services.AddSingleton(mapperConfig.CreateMapper());

在我的AutoMapper配置文件中:

CreateMap<ProjectActivity, Activity>()
        .ForMember(dest => dest.ActivityName, opt => opt.MapFrom(src => src.Name))
        .ValidateMemberList(MemberList.None);

我想知道 .ValidateMemberList(MemberList.None).ForAllOtherMembers(x => x.Ignore()) 之间的区别是什么? - Nicholas Petersen

1
关于忽略许多成员的唯一信息是这个线程 - http://groups.google.com/group/automapper-users/browse_thread/thread/9928ce9f2ffa641f。我认为您可以使用ProvidingCommonBaseClassConfiguration中使用的技巧来忽略类似类的常见属性。
而且没有关于“忽略其余部分”功能的信息。我以前看过代码,对我来说似乎很难添加这样的功能。此外,您可以尝试使用某些属性并用它标记被忽略的属性,并添加一些通用/常见代码以忽略所有标记的属性。


1
也许一种方法是使用ForAllMembers方法并实现自己的IMemberConfigurationExpression,它接收一个字符串,其中包含不应被忽略的属性名称,然后遍历其余的属性并调用Ignore()。只是一个想法,我不确定它是否可行。 - Igor Brejc
是的,这也可以起作用,但这种方法比使用属性更棘手,但它提供了更多的灵活性。遗憾的是,没有银弹 :(. - zihotki

1
我知道这是一个旧问题,但是在你的问题中 @jmoerdyk:
“如何在Profile的链式CreateMap()表达式中使用它?”
你可以在Profile的构造函数中像这样使用 answer
this.IgnoreUnmapped();
CreateMap<TSource, Tdestination>(MemberList.Destination)
.ForMember(dest => dest.SomeProp, opt => opt.MapFrom(src => src.OtherProp));

0
你可以使用ForAllMembers,然后只覆盖需要的部分,像这样。
public static IMappingExpression<TSource, TDest> IgnoreAll<TSource, TDest>(this IMappingExpression<TSource, TDest> expression)
        {
            expression.ForAllMembers(opt => opt.Ignore());
            return expression;
        }

注意,它会忽略所有内容,如果您不添加自定义映射,则已经被忽略并且将无法工作

另外,我想说,如果您对AutoMapper进行单元测试,并测试了所有模型及其所有属性是否正确映射,则不应使用此类扩展方法

您应该明确写出要忽略的内容


0
如果我们只想映射源对象的属性,我们可以使用map方法的以下重载:
DestSampleObjetc dest = new();    
dest = mapper.Map(source, dest);

0
通常情况下你不需要这个。我有一个临时配置,所以我用了这个。
 cfg.ShouldMapProperty = p => false;

然后所有的显式映射都正常工作。 Automapper 12+。

-1

版本12 这是一段极其糟糕的代码,但它解决了紧急工作。

      CreateMap<RepairPutRequest, Repair>(MemberList.None) 
                .ForAbdusselamMember(x => x.ClosedLostTimeId, y => y.MapFrom(z => z.ClosedLostTimeId))
                .ForAbdusselamMember(x => x.Explanation, y => y.MapFrom(z => z.Explanation)).IgnoreUnmapped()
                ; 

  public static class MappingExtensions
    {
        public static Dictionary<string, List<string>> list = new Dictionary<string, List<string>>();
        public static IMappingExpression<TSource, TDestination> IgnoreUnmapped<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
        {
             var sourceType = typeof(TSource);
            var destinationType = typeof(TDestination);
            var key =$"{sourceType.FullName}__||__{destinationType.FullName}"   ;
            var t = list[key];
            foreach (var item in destinationType.GetProperties())
            {
                if (!t.Contains(item.Name)) {
                    expression.ForMember(item.Name, x => x.Ignore());
                    }
            }
            return expression;
        }
        public static IMappingExpression<TSource, TDestination> ForAbdusselamMember<TSource, TDestination, TMember>(this IMappingExpression<TSource, TDestination> expression,
            Expression<Func<TDestination, TMember>> destinationMember, Action<IMemberConfigurationExpression<TSource, TDestination, TMember>> memberOptions )
        {
            var sourceType = typeof(TSource);
            var destinationType = typeof(TDestination);
            var key = $"{sourceType.FullName}__||__{destinationType.FullName}";
            if (!list.ContainsKey(key))
            {
                list[key]=new List<string>();
            }
            expression.ForMember(destinationMember, memberOptions);
            var memberInfo = ReflectionHelper.FindProperty(destinationMember);

            list[key].Add(memberInfo.Name);
            return expression;
        }
          
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接