Programing

AutoMapper : "나머지 무시"?

lottogame 2020. 5. 11. 07:59
반응형

AutoMapper : "나머지 무시"?


명시 적으로 매핑 된 속성을 제외한 모든 속성을 무시하도록 AutoMapper에 지시하는 방법이 있습니까?

외부에서 변경 될 수있는 외부 DTO 클래스가 있으며 새 속성을 추가하면 내 객체에 매핑하려고 할 때 기능 (예외 원인)이 손상되므로 명시 적으로 무시할 각 속성을 지정하지 마십시오.


이것은 내가 작성한 확장 방법으로 대상의 기존 속성이 아닌 모든 속성을 무시합니다. 질문이 2 세 이상이므로 여전히 유용한 지 확실하지 않지만 많은 수동 무시 호출을 추가 해야하는 동일한 문제가 발생했습니다.

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()매핑되지 않은 속성을 찾고 자동으로 무시 하는 솔루션이 있습니다. 더 강력한 솔루션 인 것 같습니다.


기존지도를 덮어 쓰지 않도록 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();

내가 이해 한 바에 따르면 소스에 매핑 된 필드가없는 대상에 필드가 있으므로 매핑되지 않은 대상 필드를 무시하는 방법을 찾고 있습니다.

이러한 확장 방법을 구현하고 사용하는 대신 간단하게 사용할 수 있습니다.

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

이제 automapper는 모든 소스 필드가 맵핑되었지만 다른 방식으로 맵핑되지 않았 음을 검증해야한다는 것을 알고 있습니다.

다음을 사용할 수도 있습니다.

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

나는 이것을 다음과 같이 할 수 있었다.

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을 사용하고 있습니다.


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하고이 클래스에 대한 변경 빈도가 정해진 후 되돌아 와서이를 명시 하는 것이 현명 할 수 있습니다 .


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

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

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):

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

For those who are using the non-static API in version 4.2.0 and above, the following extension method (found here in the AutoMapperExtensions class) can be used:

// 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.FindTypeMapFor will no longer work, hence the use of the expression.TypeMap field.


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

.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!


I have updated Robert Schroeder's answer for AutoMapper 4.2. With non-static mapper configurations, we can't use Mapper.GetAllTypeMaps(), but the expression has a reference to the required 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;
}

How would you prefer to specify that certain members be ignored? Is there a convention, or base class, or attribute you would like to apply? Once you get into the business of specifying all the mappings explicitly, I'm not sure what value you'd get out of AutoMapper.


This seems an old question but thought I would post my answer for anyone else looking like I was.

I use ConstructUsing, object initializer coupled with ForAllMembers ignore e.g

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

The only infromation about ignoring many of members is this thread - http://groups.google.com/group/automapper-users/browse_thread/thread/9928ce9f2ffa641f . I think you can use the trick used in ProvidingCommonBaseClassConfiguration to ignore common properties for similar classes.
And there is no information about the "Ignore the rest" functionality. I've looked at the code before and it seems to me that will be very and very hard to add such functionality. Also you can try to use some attribute and mark with it ignored properties and add some generic/common code to ignore all marked properties.


I know this is an old question, but @jmoerdyk in your question:

How would you use this in a chained CreateMap() expression in a Profile?

you can use this answer like this inside the Profile ctor

this.IgnoreUnmapped();
CreateMap<TSource, Tdestination>(MemberList.Destination)
.ForMember(dest => dest.SomeProp, opt => opt.MapFrom(src => src.OtherProp));

You can use ForAllMembers, than overwrite only needed like this

public static IMappingExpression<TSource, TDest> IgnoreAll<TSource, TDest>(this IMappingExpression<TSource, TDest> expression)
        {
            expression.ForAllMembers(opt => opt.Ignore());
            return expression;
        }

Be carefull, it will ignore all, and if you will not add custom mapping, they are already ignore and will not work

also, i want to say, if you have unit test for AutoMapper. And you test that all models with all properties mapped correctly you shouldn't use such extension method

you should write ignore's explicitly


In version of 3.3.1 you simply can use IgnoreAllPropertiesWithAnInaccessibleSetter() or IgnoreAllSourcePropertiesWithAnInaccessibleSetter() methods.

참고URL : https://stackoverflow.com/questions/954480/automapper-ignore-the-rest

반응형