AutoMapper:“忽略其余部分”吗?


205

有没有办法告诉AutoMapper忽略除显式映射的那些属性以外的所有属性?

我有可能从外部更改的外部DTO类,并且我想避免指定要显式忽略的每个属性,因为添加新属性会在尝试将它们映射到我自己的对象时破坏功能(导致异常)。


1
通过使用ValueInjecter valueinjecter.codeplex.com/documentation,您可以创建具有其映射算法并在特定属性之间进行映射的ValueInjection,而不必关心其余属性
Omu,2010年

24
对于使用Automapper>版本5的用户,请跳过以查看详细的答案.ForAllOtherMembers(opts => opts.Ignore())
Jack Ukleja

@Schneider“ .ForAllOtherMembers(opts => opts.Ignore())”与扩展名“ IgnoreAllNonExisting”不同,主要区别在于,如果未明确配置属性,则使用“ .ForAllOtherMembers(opts => opts.Ignore( ))“,您将不会获得任何映射。在没有显式配置属性的情况下使用“ IgnoreAllNonExisting”,您仍然会获得一些带有值的属性映射(具有相同名称的属性)。

是。答案就是ForAllOtherMembers。IgnoreUnmapped答案除了导致config-valid-assert传递外,什么也不做,因为未映射的成员仍然会被忽略。
6

值得一提的是,这样做时,您显式隐藏了要映射的类中可能相关或重要的更改。每当映射的类发生更改时,对每个属性进行显式映射都会使您的测试失败,从而迫使您对其进行正确评估。(鉴于您有一个进行AssertConfigurationIsValid()呼叫的测试)因此,我将“忽略其余部分”视为反模式。
阿夫·史塔德

Answers:


83

这是我写的扩展方法,它忽略了目标上所有不存在的属性。不知道它是否仍然有用,因为这个问题已有两年多了,但是我遇到了同样的问题,必须添加大量手动忽略调用。

public static IMappingExpression<TSource, TDestination> IgnoreAllNonExisting<TSource, TDestination>
(this IMappingExpression<TSource, TDestination> expression)
{
    var flags = BindingFlags.Public | BindingFlags.Instance;
    var sourceType = typeof (TSource);
    var destinationProperties = typeof (TDestination).GetProperties(flags);

    foreach (var property in destinationProperties)
    {
        if (sourceType.GetProperty(property.Name, flags) == null)
        {
            expression.ForMember(property.Name, opt => opt.Ignore());
        }
    }
    return expression;
}

用法:

Mapper.CreateMap<SourceType, DestinationType>()
                .IgnoreAllNonExisting();

更新:显然,如果您有自定义映射,这将无法正常工作,因为它会覆盖它们。我想如果先调用IgnoreAllNonExisting然后再调用自定义映射,它仍然可以工作。

schdr有一个解决方案(作为对此问题的解答),该解决方案Mapper.GetAllTypeMaps()用于找出未映射的属性并自动忽略它们。对我来说似乎是一个更强大的解决方案。


我已经有一段时间没有使用AutoMapper了,但是如果它对您有用,我会接受您的回答。
Igor Brejc

2
谢谢!!我发现这非常方便。在我的情况下,单独忽略属性无法达到使用自动映射器的目的。
Daniel Robinson

对于没有覆盖问题的问题,请参见下一个答案
Jason Coyne 2012年

3
此方法应该在autoMapper本机代码上!很好谢谢!
Felipe Oriani 2013年

2
仅供参考,吉米本人(AutoMapper的作者)在下面评论说,@ nazim的答案对于版本5+是正确的
Worthy7 '18

244

据我了解,问题是目标上的字段在源中没有映射的字段,这就是为什么您正在寻找忽略那些非映射的目标字段的原因。

除了实现和使用这些扩展方法,您可以简单地使用

Mapper.CreateMap<sourceModel, destinationModel>(MemberList.Source);  

现在,自动映射器知道仅需要验证所有源字段都已映射,而无需验证其他方式。

您还可以使用:

Mapper.CreateMap<sourceModel, destinationModel>(MemberList.Destination);  

10
这个答案应该有更多的赞誉,甚至可能被标记为答案。它解决了我的问题,同样MemberList.Destination也解决了操作问题。
Tedd Hansen

1
如果您想忽略源和目标上的少量属性,
它将不起作用

62
对于以后出现的任何人,这都是5.0的正确答案
Jimmy Bogard

3
看起来不错,但对我不起作用。。我尝试了“源”和“目的地”,但它一直抱怨缺少地图的同一属性对象
Sonic Soul

1
使用6.0.2,这不起作用。任何未从目标映射到源的属性,都将用null和0覆盖source中的属性。另外,代码无法清楚说明您正在做什么,尤其是在团队中工作时。这就是为什么我非常不喜欢此代码的原因,也是为什么我更喜欢选择的单词,如建议的答案“ IgnoreAllNonExisting”
sksallaj

222

我更新了Can Gencer的扩展程序,以不覆盖任何现有地图。

public static IMappingExpression<TSource, TDestination> 
    IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
    var sourceType = typeof (TSource);
    var destinationType = typeof (TDestination);
    var existingMaps = Mapper.GetAllTypeMaps().First(x => x.SourceType.Equals(sourceType) && x.DestinationType.Equals(destinationType));
    foreach (var property in existingMaps.GetUnmappedPropertyNames())
    {
        expression.ForMember(property, opt => opt.Ignore());
    }
    return expression;
}

用法:

Mapper.CreateMap<SourceType, DestinationType>()
                .ForMember(prop => x.Property, opt => opt.MapFrom(src => src.OtherProperty))
                .IgnoreAllNonExisting();

4
+1,感谢您发布此解决方案。当我在goo.gl/rG7SL中使用解决方案时,花了我几个小时才能弄清奇怪的错误,直到我再次迷失于此职位。
诺丁

3
我在下面推荐Yohanb的方法。在某些情况下,这似乎不起作用。
乔恩·巴克

3
可以在AutoMapper 4.2中完成吗?(Mapper.GetAllTypeMaps()已弃用)
mrmashal

14
对于AutoMapper 5+版本,只需替换Mapper.GetAllTypeMaps()为即可Mapper.Configuration.GetAllTypeMaps()。这是参考github.com/AutoMapper/AutoMapper/issues/1252

5
对于新人,请阅读此书。这个答案是针对AutoMapper 2的,在撰写此评论时,我们的版本是6。这是一个hack,使用MemberList枚举的方法更加简洁。请参阅Github第1839期和更好的解决方案。github.com/AutoMapper/AutoMapper/issues/1839这样的示例:stackoverflow.com/a/31182390/3850405
Ogglas

83

我已经可以通过以下方式做到这一点:

Mapper.CreateMap<SourceType, DestinationType>().ForAllMembers(opt => opt.Ignore());
Mapper.CreateMap<SourceType, DestinationType>().ForMember(/*Do explicit mapping 1 here*/);
Mapper.CreateMap<SourceType, DestinationType>().ForMember(/*Do explicit mapping 2 here*/);
...

注意:我正在使用AutoMapper v.2.0。


4
非常感谢!它就像一个魅力。我第一次尝试到链上的电话,但ForAllMembers只是返回void :(这不是很明显,前面的IgnoreAll可以在以后修改。
SeriousM

5
我也不喜欢这种方式..如果您有50个成员,而您想忽略25 ..那么如果您仍然要忽略25个成员,那么自动映射的意义何在?如果名称匹配,并且有不匹配的属性..为什么不明确地告诉automapper在未映射的属性上不匹配,并通过传递所有的类型呢?
sksallaj

71

AutoMapper的5.0.0-beta-1版本引入了ForAllOtherMembers扩展方法,因此您现在可以执行以下操作:

CreateMap<Source, Destination>()
    .ForMember(d => d.Text, o => o.MapFrom(s => s.Name))
    .ForMember(d => d.Value, o => o.MapFrom(s => s.Id))
    .ForAllOtherMembers(opts => opts.Ignore());

请注意,显式映射每个属性有一个优势,因为您永远不会遇到忘记映射属性时出现的无提示映射失败问题。

也许在您的情况下,明智的做法是忽略所有其他成员,并添加一个,TODO以便在此类更改的频率确定下来之后使它们明确显示。


3
令人惊讶的是,直到版本5为止。看看这个问题有多少票-投票和尝试的答案...我想知道Automapper的治理有问题吗?
Jack Ukleja '16

谢谢您,我花了一点时间向下滚动到它,但是这样做很完美。
cobolstinks

2
您甚至可以将ForAllOtherMembers行放在最前面,并且一切都会正常进行,如果您具有某种基类配置,那么这很好。
N73k

现在,这是首选方法。想知道OP是否可以更改接受的答案?
Chase Florell's

1
是否有等效项可以忽略源对象中的属性?像ForAllOtherSourceMembers什么?
SuperJMN

44

从AutoMapper 5.0开始,该.TypeMap属性IMappingExpression消失了,这意味着4.2解决方案不再起作用。我创建了一个使用原始功能但使用不同语法的解决方案:

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Src, Dest>();
    cfg.IgnoreUnmapped();        // Ignores unmapped properties on all maps
    cfg.IgnoreUnmapped<Src, Dest>();  // Ignores unmapped properties on specific map
});

// or add  inside a profile
public class MyProfile : Profile
{
   this.IgnoreUnmapped();
   CreateMap<MyType1, MyType2>();
}

实现方式:

public static class MapperExtensions
{
    private static void IgnoreUnmappedProperties(TypeMap map, IMappingExpression expr)
    {
        foreach (string propName in map.GetUnmappedPropertyNames())
        {
            if (map.SourceType.GetProperty(propName) != null)
            {
                expr.ForSourceMember(propName, opt => opt.Ignore());
            }
            if (map.DestinationType.GetProperty(propName) != null)
            {
                expr.ForMember(propName, opt => opt.Ignore());
            }
        }
    }

    public static void IgnoreUnmapped(this IProfileExpression profile)
    {
        profile.ForAllMaps(IgnoreUnmappedProperties);
    }

    public static void IgnoreUnmapped(this IProfileExpression profile, Func<TypeMap, bool> filter)
    {
        profile.ForAllMaps((map, expr) =>
        {
            if (filter(map))
            {
                IgnoreUnmappedProperties(map, expr);
            }
        });
    }

    public static void IgnoreUnmapped(this IProfileExpression profile, Type src, Type dest)
    {
        profile.IgnoreUnmapped((TypeMap map) => map.SourceType == src && map.DestinationType == dest);
    }

    public static void IgnoreUnmapped<TSrc, TDest>(this IProfileExpression profile)
    {
        profile.IgnoreUnmapped(typeof(TSrc), typeof(TDest));
    }
}

3
您将如何在链式CreateMap<TSource,TDest>()表达式中使用它Profile
jmoerdyk

2
谢谢你 GetUnmappedPropertyNames方法在源和目标上都返回所有未映射的属性名称,这似乎在反向映射上中断了,因此我不得不对IgnoreUnmapped进行少量更改,以检查未映射的属性是否在源或目标上并且忽略相应地。这是展示问题和更新的小提琴:dotnetfiddle.net/vkRGJv
Mun

1
我已经更新了答案,以包括您的发现-我不使用源映射,所以没有遇到这个问题!谢谢。
理查德

1
在没有可用反射的情况下,这在PCL上不起作用,GetProperty(propName)不存在。
George Taskos

我看不出这是如何解决这个问题的,甚至没有任何作用。未映射的属性已经被忽略-因为它们是未映射的。张贴者说:“除非明确映射,否则如何忽略道具”。这意味着,如果我有Src.MyProp和Dest.MyProp,则除非对MyProp明确调用MapFrom&ForMember,否则应忽略该映射。因此,默认映射需要忽略。该解决方案唯一要做的就是使config-valid-assert事情通过-无论如何您都不需要映射才能工作。
6

17

自问这个问题以来已有好几年了,但是使用当前版本的AutoMapper(3.2.1),这种扩展方法对我来说似乎更干净:

public static IMappingExpression<TSource, TDestination> IgnoreUnmappedProperties<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
    var typeMap = Mapper.FindTypeMapFor<TSource, TDestination>();
    if (typeMap != null)
    {
        foreach (var unmappedPropertyName in typeMap.GetUnmappedPropertyNames())
        {
            expression.ForMember(unmappedPropertyName, opt => opt.Ignore());
        }
    }

    return expression;
}

16

对于那些谁正在使用的非静态API在4.2.0及以上版本,下面的扩展方法(发现这里AutoMapperExtensions类)可用于:

// from http://stackoverflow.com/questions/954480/automapper-ignore-the-rest/6474397#6474397
public static IMappingExpression IgnoreAllNonExisting(this IMappingExpression expression)
{
    foreach(var property in expression.TypeMap.GetUnmappedPropertyNames())
    {
        expression.ForMember(property, opt => opt.Ignore());
    }
    return expression;
}

这里重要的是,一旦删除了静态API,诸如之类的代码Mapper.FindTypeMapFor将不再起作用,因此将使用该expression.TypeMap字段。


7
从5.0版开始,expression.TypeMap不再可用。这是我针对5.0
理查德(Richard)

我不得不用来public static IMappingExpression<TSource, TDestination> IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)解决类型问题。
尼克M

16

对于Automapper 5.0,为了跳过所有未映射的属性,您只需要放入

.ForAllOtherMembers(x => x.Ignore());

在您的个人资料末尾。

例如:

internal class AccountInfoEntityToAccountDtoProfile : Profile
{
    public AccountInfoEntityToAccountDtoProfile()
    {
        CreateMap<AccountInfoEntity, AccountDto>()
           .ForMember(d => d.Id, e => e.MapFrom(s => s.BankAcctInfo.BankAcctFrom.AcctId))
           .ForAllOtherMembers(x=>x.Ignore());
    }
}

在这种情况下,只会解析输出对象的Id字段,其他所有字段都将被跳过。像魅力一样工作,似乎我们不再需要任何棘手的扩展!


10

我已经更新了Robert Schroeder对于AutoMapper 4.2的答案。对于非静态映射器配置,我们不能使用Mapper.GetAllTypeMaps(),但是expression具有对必填项的引用TypeMap

public static IMappingExpression<TSource, TDestination> 
    IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
    foreach (var property in expression.TypeMap.GetUnmappedPropertyNames())
    {
        expression.ForMember(property, opt => opt.Ignore());
    }
    return expression;
}

在AutoMapper 5.0中不起作用。IMappingExpression的.TypeMap属性不可用。对于5. +版本,请参见Richard答案中的
Michael Freidgeim

与AM 4.2配合使用
Leszek P '18

8

您希望如何指定某些成员被忽略?是否要使用约定,基类或属性?一旦您从事显式指定所有映射的业务,就不确定从AutoMapper获得什么价值。


吉米,您有一个明确的观点。至于如何以一种最优雅的方式实现这一目标的方法:在这种情况下,基类和属性将不起作用,因为目标类实际上不受我的控制-它们是从XSD数据协定自动生成的,因此在每个生成周期后手动编辑此代码。我想解决方案取决于具体情况。也许类似于Windsor Castle的流利界面提供了选择在容器中注册哪些组件的解决方案?
Igor Brejc,2009年

啊,这现在更有意义。这是一个有趣的功能,我将在2.1时间范围内介绍该功能。
Jimmy Bogard 2010年

2
仅具有一个可“忽略”所有不存在的字段的可配置值怎么样。
里卡多·桑切斯

6
这不是问题的答案。
user2864740 2015年

嗨,吉米,你是作者,对吗?我希望能够忽略所有不存在的属性,将其作为默认行为(可以由标志控制)。另外,我从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());

1

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


1
也许一种方法是使用ForAllMembers方法并实现我自己的IMemberConfigurationExpression,该方法接收一个字符串,其中包含不应忽略的那些属性的属性名称,然后遍历其余所有属性并调用Ignore()。只是一个想法,我不确定是否行得通。
Igor Brejc,2009年

是的,它也可以工作,但是此方法比使用属性要棘手,但它提供了更大的灵活性。这是一个遗憾,有没有银弹:(。
zihotki

1

我知道这是一个老问题,但您的问题中有@jmoerdyk:

您如何在Profile的链式CreateMap()表达式中使用它?

您可以在Profile ctor中使用这样的答案

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进行单元测试。并且您测试了所有属性正确映射的模型,您不应该使用这种扩展方法

你应该显式地写ignore


-1

当前(版本9)忽略目标类型中不存在的属性的解决方案是创建一个翻转映射并将其反转:

var config = new MapperConfiguration(cfg => {
  cfg.CreateMap<TheActualDestinationType, TheActualSourceType>().ReverseMap();
});

-2

在3.3.1版本中,您可以简单地使用IgnoreAllPropertiesWithAnInaccessibleSetter()IgnoreAllSourcePropertiesWithAnInaccessibleSetter()方法。


6
按照原始发布者的问题,这不起作用。这些方法仅忽略受保护的属性或私有属性,而不忽略源中缺少但目标类型中存在的属性。
2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.