.net AutoMapper:“忽略其余部分”?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/954480/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 12:48:56  来源:igfitidea点击:

AutoMapper: "Ignore the rest"?

.netautomapper

提问by Igor Brejc

Is there a way to tell AutoMapper to ignore all of the properties except the ones which are mapped explicitly?

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

I have external DTO classes which are likely to change from the outside and I want to avoid specifying each property to be ignored explicitly, since adding new properties will break the functionality (cause exceptions) when trying to map them into my own objects.

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

采纳答案by Can Gencer

This is an extension method I wrote that ignores all non existing properties on the destination. Not sure if it will still be useful as the question is more than two years old, but I ran into the same issue having to add a lot of manual Ignore calls.

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

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;
}

Usage:

用法:

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

UPDATE: Apparently this does not work correctly if you have custom mappings because it overwrites them. I guess it could still work if call IgnoreAllNonExisting first and then the custom mappings later.

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

schdr has a solution (as an answer to this question) which uses Mapper.GetAllTypeMaps()to find out which properties are unmapped and auto ignore them. Seems like a more robust solution to me.

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

回答by Nazim Hafeez

From what I understood the question was that there are fields on the destination which doesn't have a mapped field in the source, which is why you are looking for ways to Ignore those non mapped destination fields.

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

Instead of implementing and using these extension method you could simply use

您可以简单地使用,而不是实现和使用这些扩展方法

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

Now the automapper knows that it needs to only validate that all the source fields are mapped but not the other way around.

现在自动映射器知道它只需要验证所有源字段是否都已映射,而不是相反。

You can also use:

您还可以使用:

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

回答by Robert Schroeder

I've updated Can Gencer's extension to not overwrite any existing maps.

我已经更新了 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;
}

Usage:

用法:

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

回答by Yohanb

I've been able to do this the following way:

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

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*/);
...

Note: I'm using AutoMapper v.2.0.

注意:我使用的是 AutoMapper v.2.0。

回答by ajbeaven

Version 5.0.0-beta-1 of AutoMapper introduces the ForAllOtherMembersextension method so you can now do this:

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());

Be aware that there is an advantage to explicitly mapping each property as you will never get problems of mapping failing silently that arise when you forget to map a property.

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

Perhaps in your case it might be wise to ignore all other members and add a TODOto come back and make these explicit after the frequency of changes to this class settle down.

也许在您的情况下,忽略所有其他成员并添加一个TODO返回并在此类更改的频率稳定后使这些成员明确可能是明智的。

回答by Richard

As of AutoMapper 5.0, the .TypeMapproperty on IMappingExpressionis gone, meaning the 4.2 solution no longer works. I've created a solution which uses the original functionality but with a different syntax:

从 AutoMapper 5.0 开始,.TypeMap属性 onIMappingExpression消失了,这意味着 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>();
}

Implementation:

执行:

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));
    }
}

回答by Iravanchi

There's been a few years since the question has been asked, but this extension method seems cleaner to me, using current version of AutoMapper (3.2.1):

自从提出这个问题已经有几年了,但是使用当前版本的 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;
}

回答by nick_w

For those who are using the non-static APIin version 4.2.0 and above, the following extension method (found herein the AutoMapperExtensionsclass) can be used:

对于那些谁正在使用的非静态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;
}

The important thing here is that once the static API is removed, code such as Mapper.FindTypeMapForwill no longer work, hence the use of the expression.TypeMapfield.

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

回答by framerelay

For Automapper 5.0 in order to skip all unmapped properties you just need put

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

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

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

at the end of your profile.

在您个人资料的末尾。

For example:

例如:

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());
    }
}

In this case only Id field for output object will be resolved all other will be skipped. Works like a charm, seems we don't need any tricky extensions anymore!

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

回答by mrmashal

I have updated Robert Schroeder's answer for AutoMapper 4.2. With non-static mapper configurations, we can't use Mapper.GetAllTypeMaps(), but the expressionhas a reference to the required TypeMap:

我已经更新了 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;
}