AutoMapper: “Kalanı yoksay” mı?


206

AutoMapper'a açıkça eşlenenler dışındaki tüm özellikleri yok saymasını söylemenin bir yolu var mı?

Dışarıdan değişmesi muhtemel dış DTO sınıfları var ve yeni özellikler ekleyerek kendi nesneleri içine eşlemeye çalışırken işlevselliği (istisnalara neden) kıracak çünkü açıkça, her özellik belirtilmesini önlemek istiyorum.


1
ValueInjecter valueinjecter.codeplex.com/documentation ile eşleme alghorithm'i ve belirli özellikler arasında eşlemesi olan ValueInjections oluşturursunuz ve özelliklerin geri kalanıyla ilgilenmezler
Omu

24
Automapper> sürüm 5'i kullananlar için, ayrıntılarını görmek üzere aşağı atlayın.ForAllOtherMembers(opts => opts.Ignore())
Jack Ukleja

@Schneider ".ForAllOtherMembers (opts => opts.Ignore ())", burada "IgnoreAllNonExisting" uzantısıyla farklıdır, ana fark özelliği ".ForAllOtherMembers (opts => opts.Ignore ( )) "hiçbir şey eşlenmez. config özelliği olmadan açıkça "IgnoreAllNonExisting" kullanın, yine de bazı özellik eşlemeli (aynı ada sahip özellikler) değer alırsınız.
Ejderha

Evet. Cevap ForAllOtherMembers. IgnoreUnmapped yanıtları, config-valid-assert öğesinin geçmesine neden olması dışında hiçbir şey yapmaz, çünkü eşleştirilmemiş üyeler yine de yok sayılır.
N73k

Bunu yaparken, eşlenen sınıflarda potansiyel olarak alakalı veya önemli değişiklikleri açıkça gizlediğinizi belirtmek gerekir. Her mülk için açık eşlemeler olması, eşlenen sınıf her değiştiğinde sizi kırık bir teste tabi tutar ve doğru değerlendirmeye zorlar. ( AssertConfigurationIsValid()Çağrıyı yapan bir testiniz olduğu göz önüne alındığında ) Bu nedenle, "Gerisini yoksay" seçeneğini bir antipattern olarak görüyorum.
Arve Systad

Yanıtlar:


83

Bu, hedefte var olmayan tüm özellikleri yok sayan bir uzantı yöntemidir. Sorunun iki yıldan daha eski olduğu için hala yararlı olup olmayacağından emin değilim, ancak aynı sorunu çok sayıda manuel Yoksay çağrısı eklemek zorunda kaldım.

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

Kullanımı:

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

GÜNCELLEME : Görünüşe göre, bunların üzerine yazdığı için özel eşlemeleriniz varsa doğru şekilde çalışmaz. Önce IgnoreAllNonExisting ve daha sonra özel eşlemeler çağırırsanız yine de işe yarayabilir sanırım.

schdr, Mapper.GetAllTypeMaps()hangi özelliklerin eşlenmemiş olduğunu bulmak ve bunları otomatik olarak yoksaymak için kullanılan bir çözüme (bu sorunun cevabı olarak) sahiptir . Bana daha sağlam bir çözüm gibi görünüyor.


AutoMapper'ı bir süredir kullanmadım, ancak sizin için işe yararsa cevabınızı kabul edeceğim :).
Igor Brejc

2
Teşekkürler!! Bunu çok kullanışlı buldum. Özellikleri bireysel olarak görmezden gelmek, durumumda otomatik haritayı kullanma amacını yendi.
Daniel Robinson

Üzerine yazma sorunu olmayan biri için bir sonraki cevaba bakın
Jason Coyne

3
Bu yöntem autoMapper yerel kodunda olmalıdır! Çok güzel teşekkür ederim!
Felipe Oriani

2
Jimmy kendisi (AutoMapper'in yazarı) FYI, @
nazim'in

244

Soruyu anladığım kadarıyla, hedefte kaynakta eşlenmiş bir alanı olmayan alanlar olduğu, bu yüzden bu eşlenmemiş hedef alanlarını yoksaymanın yollarını aradığınız oldu.

Bu uzantı yöntemini uygulamak ve kullanmak yerine,

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

Artık otomatikleştirici, yalnızca tüm kaynak alanlarının eşlendiğini doğrulamak gerektiğini biliyor, ancak bunun tersi değil.

Ayrıca kullanabilirsiniz:

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

10
Bu cevabın daha fazla oyu olmalı, hatta cevap olarak işaretlenmelidir. MemberList.DestinationSorunumu çözdü ve benzer şekilde ops problemini çözecekti.
Tedd Hansen

1
Hem kaynak hem de hedefte birkaç özelliği yoksaymak istiyorsanız işe yaramaz :)
RealWillyWoka

62
Daha sonra gelen herkese, BU 5.0 İÇİN DOĞRU CEVAPTIR
Jimmy Bogard

3
şık görünüyor ama benim için işe yaramadı .. Kaynak ve Hedef çalıştı, ama aynı özellik nesnesi hakkında bir harita eksik şikayet ediyor
Sonic Soul

1
6.0.2 kullanıldığında bu çalışmaz. Hedeften kaynağa eşlenmeyen tüm özellikler, kaynaktaki özelliklerin üzerine null ve 0s yazar. Ayrıca kod, özellikle de bir ekipte çalışıyorsanız, yaptığınız işi netleştirmez. Bu yüzden bu kodu çok sevmiyorum ve neden önerilen cevap "IgnoreAllNonExisting" gibi seçim kelimeleri tercih ediyorum
sksallaj

222

Can Gencer'in uzantısını mevcut haritaların üzerine yazmayacak şekilde güncelledim.

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

Kullanımı:

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

4
+1, Bu çözümü gönderdiğiniz için teşekkür ederiz. Bu yazıya tekrar rastlamak kadar goo.gl/rG7SL'de çözüm kullandığımda garip bir hata bulmam saatler sürdü .
Nordin

3
Bu konuda Yohanb'ın yöntemini aşağıda öneriyorum. Bunun işe yaramadığı bazı köşe vakaları var.
Jon Barker

3
Bu, AutoMapper 4.2 ile yapılabilir mi? (Kullanımdan Mapper.GetAllTypeMaps()kaldırıldı)
mrmashal

14
AutoMapper 5+ sürümü için sadece yerini Mapper.GetAllTypeMaps()ile Mapper.Configuration.GetAllTypeMaps(). İşte github.com/AutoMapper/AutoMapper/issues/1252 referansı
Sergey G.

5
Bunu okuyan yeni insanlar için. Bu cevap AutoMapper 2 içindir ve bu yorumu yazarken 6. versiyondayız. Bu bir hack ve MemberList enum'u kullanmak için çok daha temiz bir yol. Bkz Github sayı 1839 ve daha iyi bir çözüm. github.com/AutoMapper/AutoMapper/issues/1839 Yani örnek: stackoverflow.com/a/31182390/3850405
Ogglas

83

Bunu şu şekilde yapabildim:

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

Not: AutoMapper v.2.0 kullanıyorum.


4
çok teşekkür ederim! mucizevi şekilde çalışır. Ben ilk aramalar zincir çalıştı ama ForAllMembers sadece geçersiz dönüş :(. Önceki bir IgnoreAll daha sonra değiştirilebilir açık değildi.
SeriousM

5
Ben de bu şekilde sevmiyorum .. 50 üyeniz varsa ve 25'i görmezden gelmek istiyorsanız .. o zaman hala 25 üyeyi görmezden gelmeniz gerekiyorsa otomatik haritanın anlamı nedir. İsimler eşleşiyorsa ve eşleşmeyen özellikler varsa .. neden automapper'a eşlenmemiş özelliklerde eşleşmediğini ve tüm yazarak iletmeyi açıklamıyorsunuz?
sksallaj

71

AutoMapper'ın 5.0.0-beta-1 sürümü ForAllOtherMembers, şimdi bunu yapabilmeniz için uzantı yöntemini sunar:

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

Bir mülkü eşlemeyi unuttuğunuzda ortaya çıkan sessizce haritalama sorunlarıyla karşılaşmayacağınız için, her mülkü açıkça eşlemenin bir avantajı olduğunu unutmayın.

Belki de sizin durumunuzda, diğer tüm üyeleri görmezden TODOgelmek ve geri gelmek için bir ek eklemek akıllıca olabilir ve bu sınıftaki değişikliklerin sıklığı yerleştikten sonra bunları açık bir şekilde ortaya koyabilirsiniz.


3
Şaşırtıcı bu sürüm 5'e kadar sürdü. Bakın kaç soru-bu soruya cevaplar ve cevaplar denedi ... Automapper'in yönetişimi ile ilgili yanlış bir şey mi merak ediyorum?
Jack Ukleja

Bunun için teşekkür ederim, aşağı kaydırmam için biraz zamanımı aldı ama bu, ama mükemmel çalışıyor.
cobolstinks

2
ForAllOtherMembers satırını ilk sıraya koyabilirsiniz ve her şey aynı şekilde çalışır, bu da bir tür temel sınıf yapılandırmanız varsa iyidir.
N73k

Şimdi tercih edilen yaklaşım budur. OP'nin kabul edilen cevabı değiştirip değiştiremeyeceğini merak ediyor musunuz?
Chase Florell

1
Kaynak nesnedeki özellikleri yok saymak için bir eşdeğer var mı? Gibi bir şey ForAllOtherSourceMembers?
SuperJMN

44

AutoMapper 5.0'dan itibaren, .TypeMapözellik kaldırıldı IMappingExpression, yani 4.2 çözümü artık çalışmıyor. Orijinal işlevselliği kullanan ancak farklı bir sözdizimiyle bir çözüm oluşturdum:

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

Uygulama:

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
Bunu zincirleme bir CreateMap<TSource,TDest>()ifadede a Profile.
jmoerdyk

2
Bunun için teşekkürler. GetUnmappedPropertyNames yöntemi, tüm eşlenmemiş özellik adlarını, hem kaynak hem de hedef üzerinde, ters bir haritada kesilmiş gibi görünüyor, bu nedenle, eşlenmemiş özelliğin kaynak veya hedefte olup olmadığını denetlemek ve yoksaymak için IgnoreUnmapped üzerinde küçük bir değişiklik yapmak zorunda kaldım buna göre. İşte sorunu ve güncellemeyi gösteren bir keman: dotnetfiddle.net/vkRGJv
Mun

1
Cevabınızı bulgularınızı içerecek şekilde güncelledim - Kaynak eşlemelerini kullanmıyorum, bu yüzden buna rastlamamıştım! Teşekkürler.
Richard

1
Bu, yansıma olmadan PCL'de çalışmaz, GetProperty (propName) mevcut değildir.
George Taskos

Bunun soruya nasıl bir çözüm olduğunu veya bunun nasıl bir şey yaptığını görmüyorum. Eşlenmemiş özellikler, zaten eşleştirilmedikleri için yok sayılır . Poster, " açık bir şekilde haritalandırılmadığı sürece sahne aksesuarlarını nasıl görmezden gelirsiniz? " Bu, Src.MyProp ve Dest.MyProp'ım varsa, MapProm & MyMrop için ForMember'e açık bir çağrı olmadığı sürece bu eşlemenin göz ardı edilmesi gerektiği anlamına gelir. Bu nedenle, varsayılan eşlemenin yoksayılması gerekir. Bu çözümün yaptığı tek şey, eşlemenin çalışması için herhangi bir şeye ihtiyacınız olmayan config-valid-assert öğesinin geçmesine neden olmaktır.
N73k

17

Sorunun sorulmasından bu yana birkaç yıl geçti, ancak bu uzantı yöntemi AutoMapper'in (3.2.1) geçerli sürümünü kullanarak bana daha temiz görünüyor:

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

Kullandığınız olanlar için statik olmayan API , yukarıdaki (bulunan aşağıdaki uzatma yöntemi sürümü 4.2.0 ve burada yer AutoMapperExtensionskullanılabilir sınıfına):

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

Burada önemli olan, statik API kaldırıldıktan sonra, kodun Mapper.FindTypeMapForartık çalışmaz olması, dolayısıyla expression.TypeMapalanın kullanılmasıdır.


7
5.0 itibariyle expression.TypeMapartık mevcut değil. İşte 5.0 için çözümüm
Richard

public static IMappingExpression<TSource, TDestination> IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)Tür sorunlarını gidermek için kullanmak zorunda kaldım .
Nick M

16

Automapper 5.0 için tüm eşlenmemiş özellikleri atlamak için tek yapmanız gereken

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

profilinizin sonunda.

Örneğin:

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

Bu durumda, yalnızca çıktı nesnesi için Kimlik alanı çözülür ve diğer tüm alan atlanır. Bir cazibe gibi çalışıyor, artık zor uzantılara ihtiyacımız yok gibi görünüyor!


10

Robert Schroeder'in AutoMapper 4.2 yanıtını güncelledim. Statik olmayan eşleyici yapılandırmalarıyla kullanamayız Mapper.GetAllTypeMaps(), ancak expressiongerekli olanlara bir referansı vardır 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'da çalışmaz. IMappingExpression üzerindeki .TypeMap özelliği kullanılamıyor. 5. + sürümü için Richard'ın cevabındaki
Michael Freidgeim

AM 4.2 ile çalışır
Leszek P

8

Belirli üyelerin yok sayılmasını nasıl belirlersiniz? Uygulamak istediğiniz bir kongre, temel sınıf veya özellik var mı? Tüm eşlemeleri açıkça belirleme işine girdikten sonra, AutoMapper'dan hangi değeri alacağınızdan emin değilim.


Jimmy, açıklık hakkında bir fikrin var. Bunu en zarif bir şekilde nasıl elde edeceğinize gelince: temel sınıflar ve nitelikler bu durumda işe yaramaz, çünkü hedef sınıflar gerçekten benim kontrolüm altında değildir - XSD veri sözleşmesinden otomatik olarak üretilirler, bu yüzden biri Her nesil çevriminden sonra bu kodu manuel olarak düzenlemek için. Sanırım çözüm somut bir duruma bağlı. Belki de Windsor Kalesi'ne benzeyen akıcı bir arayüz, konteynere hangi bileşenlerin kaydedileceğini seçmek için bir çözüm sağlayabilir?
Igor Brejc

Ah, şimdi daha mantıklı. Bu ilginç bir özellik, buna 2.1 zaman diliminde bakacağım.
Jimmy Bogard

2
Yalnızca mevcut olmayan tüm alanları "yok sayabileceğiniz" yapılandırılabilir bir değere sahip olmaya ne dersiniz?
Ricardo Sanchez

6
Bu sorunun cevabı değil.
user2864740

Merhaba Jimmy, Sen yazarsın, değil mi? Var olmayan tüm özellikleri varsayılan bir davranış (bir bayrak tarafından kontrol edilebilir) olan yoksaymak istiyorum. Ayrıca, anlayamıyorum AutoMapper garip bir hata yaşıyorum. Bana herhangi bir ayrıntı vermiyor.
Naomi

7

Bu eski bir soru gibi görünüyor ama cevabımı sanki başka biri için göndereceğimi düşündüm.

ConstructUsing, ForAllMembers ile birleştiğinde nesne başlatıcısı yoksay örn.

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

1

Üyelerin çoğunu görmezden gelmeyle ilgili tek bilgi bu konudur - http://groups.google.com/group/automapper-users/browse_thread/thread/9928ce9f2ffa641f . Benzer sınıflar için ortak özellikleri yok saymak için ProvidingCommonBaseClassConfiguration kullanılan hile kullanabilirsiniz düşünüyorum.
Ve "Kalanı yoksay" işlevi hakkında bilgi yoktur. Daha önce koda baktım ve bana böyle bir işlevsellik eklemek çok ve çok zor olacak gibi görünüyor. Ayrıca, bazı öznitelikleri kullanmayı ve yok sayılan özelliklerle işaretlemeyi ve tüm işaretli özellikleri yok saymak için genel / ortak kod eklemeyi deneyebilirsiniz.


1
Belki de bir yol, ForAllMembers yöntemini kullanmak ve yok sayılmaması gereken bu özelliklerin özellik adlarını içeren bir dize alan kendi IMemberConfigurationExpression uygulamak ve sonra geri kalanı geçmesi ve Ignore () çağırmak olacaktır. Sadece bir fikir, işe yarayıp yaramayacağından emin değilim.
Igor Brejc

Evet, bu da işe yarayabilir, ancak bu yöntem nitelikleri kullanmaktan daha zordur, ancak daha fazla esneklik sunar. Gümüş mermi olmaması üzücü :(.
zihotki

1

Bunun eski bir soru olduğunu biliyorum, ama sorunuzda @jmoerdyk:

Bunu bir Profildeki zincirlenmiş CreateMap () ifadesinde nasıl kullanırsınız?

bu yanıtı Profil sepetinde bu şekilde kullanabilirsiniz

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

0

Sadece böyle gerekli olanların üzerine yazmak yerine ForAllMembers kullanabilirsiniz.

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

Dikkatli olun, hepsini yok sayar ve özel eşleme eklemezseniz, zaten yok sayılır ve çalışmaz

Ayrıca, AutoMapper için birim testiniz varsa, söylemek istiyorum. Ve tüm özelliklere sahip tüm modellerin doğru şekilde eşlendiğini test edersiniz, böyle bir uzantı yöntemi kullanmamalısınız

görmezden gelmelisin


-1

Hedef türünde bulunmayan özellikleri yok saymak için geçerli (sürüm 9) çözüm, döndürülmüş bir eşleme oluşturmak ve tersine çevirmektir:

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

-2

3.3.1 sürümünde IgnoreAllPropertiesWithAnInaccessibleSetter()veya IgnoreAllSourcePropertiesWithAnInaccessibleSetter()yöntemlerini kullanabilirsiniz .


6
Bu, orijinal posterin sorusuna göre çalışmaz. Bu yöntemler, kaynakta eksik olan ancak hedef türünde bulunan özellikleri değil, yalnızca korumalı veya özel özellikleri yok sayar.
Dan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.