AutoMapper कई स्रोतों से परिवर्तित होते हैं


80

मान लें कि मेरे पास दो मॉडल कक्षाएं हैं:

public class People {
   public string FirstName {get;set;}
   public string LastName {get;set;}
}

इसके अलावा एक वर्ग फोन है:

public class Phone {
   public string Number {get;set;}
}

और मैं इस तरह एक PeoplePhoneDto में परिवर्तित करना चाहते हैं:

public class PeoplePhoneDto {
    public string FirstName {get;set;}
    public string LastName {get;set;}
    public string PhoneNumber {get;set;}
}

चलो मेरे नियंत्रक में कहते हैं:

var people = repository.GetPeople(1);
var phone = repository.GetPhone(4);

// normally, without automapper I would made
return new PeoplePhoneDto(people, phone) ;

मुझे इस परिदृश्य के लिए कोई उदाहरण नहीं मिल रहा है। क्या यह संभव है ?

नोट: उदाहरण वास्तविक नहीं है, बस इस प्रश्न के लिए।


@Andrei जब मैं सहमत हूं तो यह समान लगता है, यह उस समस्या का एक अंतर है जिसे हल करने की कोशिश कर रहा है। यह भी उस सवाल से समझना मुश्किल है कि यह इस पर कैसे लागू होगा।
बार्ट कैलिक्सो

क्यों नहीं बना PeoplePhoneDtoहै एक सदस्य Peopleऔर Phoneसदस्य?
क्रश करें

क्योंकि वह वह नहीं है जिसे मैं उजागर करना चाहता हूं।
बार्ट कैलिक्सो

3
फिर से खोलने के लिए मतदान - जबकि मुझे लगता है कि stackoverflow.com/questions/12429210/… एक डुप्लिकेट है, यह (इसके एक जवाब के साथ) एक बहुत लगता है स्थानीय भी विहित माना जाता है। डुप्लिकेट प्रश्नों की मिसाल है कि यदि वे इस मामले को निपटाने के लिए पर्याप्त उत्तर नहीं दे रहे हैं तो गिनती नहीं करेंगे।
ब्रिलियनड

जवाबों:


103

आप कई स्रोतों को सीधे सिंगल डेस्टिनेशन पर मैप नहीं कर सकते हैं - आपको एक-एक करके नक्शे को लागू करना चाहिए, जैसा कि एंड्रयू व्हिटकर उत्तर में वर्णित है । तो, आपको सभी मैपिंग को परिभाषित करना होगा:

Mapper.CreateMap<People, PeoplePhoneDto>();
Mapper.CreateMap<Phone, PeoplePhoneDto>()
        .ForMember(d => d.PhoneNumber, a => a.MapFrom(s => s.Number));

फिर इनमें से किसी भी मैपिंग द्वारा डेस्टिनेशन ऑब्जेक्ट बनाएं, और अन्य मैपिंग को बनाए गए ऑब्जेक्ट पर लागू करें। और इस कदम को बहुत ही सरल विस्तार विधि से सरल बनाया जा सकता है:

public static TDestination Map<TSource, TDestination>(
    this TDestination destination, TSource source)
{
    return Mapper.Map(source, destination);
}

उपयोग बहुत सरल है:

var dto = Mapper.Map<PeoplePhoneDto>(people)
                .Map(phone);

AutoMapper पर एक एब्स्ट्रैक्शन IMapper है जो कई स्रोतों को सिंगल डेस्टिनेशन में मैप करता है।
इल्या पालकिन

@Sergey Berezovskiy, मैंने मैपिंग बनाई, PeoplePhoneDto वर्ग में विस्तार पद्धति को जोड़ा, और आपके उपयोग को कॉपी-पेस्ट किया (यानी, मुझे कॉपी-पेस्ट की गई हर चीज की आवश्यकता है), लेकिन मुझे "नो ओवरलोड ऑन मेथड मे 1 तर्क लगता है" त्रुटि मिलती है। मैं क्या खो रहा हूँ? मैं ऑटोमेपर 4.2.1 का उपयोग कर रहा हूं।
आईरडी

@HeyJude सुनिश्चित करें कि आपकी Mapएक्सटेंशन विधि उस बिंदु पर दिखाई दे रही है जिसे आप मैपिंग करते हैं (यानी निर्देश का उपयोग करके सही जोड़ा गया है)
सर्गेई बेरेज़ोवस्की

यह अच्छा है, लेकिन मैं इसका नक़ल नहीं कर पाने के कारण स्टैटिक मैप का उपयोग करना पसंद नहीं करता, इसलिए मैं ilyas इम्परर एब्सट्रैक्शन की कोशिश करूँगा
sensei

क्या यह प्रत्येक नक्शे के लिए डीटीओ क्लास को 2 बार या सिर्फ एक बार बनाएगा?
एंथिस किवरोग्लू

19

आप इसके लिए उपयोग कर सकते हैं Tuple:

Mapper.CreateMap<Tuple<People, Phone>, PeoplePhoneDto>()
    .ForMember(d => d.FirstName, opt => opt.MapFrom(s => s.Item1.FirstName))
    .ForMember(d => d.LastName, opt => opt.MapFrom(s => s.Item1.LastName))
    .ForMember(d => d.Number, opt => opt.MapFrom(s => s.Item2.Number ));

यदि आपके पास अधिक स्रोत मॉडल हैं तो आप एक अलग प्रतिनिधित्व (सूची, शब्दकोश या कुछ और) का उपयोग कर सकते हैं जो स्रोत के रूप में इन सभी मॉडलों को एक साथ इकट्ठा करेंगे।

उपर्युक्त कोड को कुछ ऑटोमैपररॉन्फ़िगरेशन फ़ाइल में प्रेफैबी को रखा जाना चाहिए, एक बार और विश्व स्तर पर सेट किया गया है और फिर जब लागू किया जाता है।

डिफ़ॉल्ट रूप से AutoMapper केवल एक डेटा स्रोत का समर्थन करता है। तो सीधे कई स्रोतों को सेट करने की कोई संभावना नहीं है (एक संग्रह में इसे लपेटे बिना) क्योंकि तब हमें कैसे पता चलेगा कि अगर दो स्रोत मॉडल में समान नाम वाले गुण हैं तो क्या होगा?

हालांकि इसे प्राप्त करने के लिए कुछ समाधान है:

public static class EntityMapper
{
    public static T Map<T>(params object[] sources) where T : class
    {
        if (!sources.Any())
        {
            return default(T);
        }

        var initialSource = sources[0];

        var mappingResult = Map<T>(initialSource);

        // Now map the remaining source objects
        if (sources.Count() > 1)
        {
            Map(mappingResult, sources.Skip(1).ToArray());
        }

        return mappingResult;
    }

    private static void Map(object destination, params object[] sources)
    {
        if (!sources.Any())
        {
            return;
        }

        var destinationType = destination.GetType();

        foreach (var source in sources)
        {
            var sourceType = source.GetType();
            Mapper.Map(source, destination, sourceType, destinationType);
        }
    }

    private static T Map<T>(object source) where T : class
    {
        var destinationType = typeof(T);
        var sourceType = source.GetType();

        var mappingResult = Mapper.Map(source, sourceType, destinationType);

        return mappingResult as T;
    }
}

और तब:

var peoplePhoneDto = EntityMapper.Map<PeoplePhoneDto>(people, phone);

लेकिन काफी ईमानदार होने के बावजूद, भले ही मैं पहले से ही कुछ वर्षों से ऑटोमैपर का उपयोग कर रहा हूं, लेकिन मुझे कई स्रोतों से मैपिंग का उपयोग करने की आवश्यकता नहीं है। ऐसे मामलों में जब उदाहरण के लिए मुझे अपने एकल दृश्य मॉडल में कई व्यावसायिक मॉडल की आवश्यकता होती है, मैं बस इन मॉडलों को दृश्य मॉडल वर्ग के भीतर एम्बेड करता हूं।

तो आपके मामले में ऐसा लगेगा:

public class PeoplePhoneDto {
    public People People { get; set; }
    public Phone Phone { get; set; }
}

3
इसलिए मुझे मैपिंग करने से पहले एक ट्यूपल बनाना होगा, मैं सोच रहा हूं कि ऑटोमैपर के वास्तविक लाभ क्या हैं ... थोड़ा ओवरकिल ध्वनि करें। क्या कोई अन्य प्रकार (टपल, डिक, आदि) बनाने से बचने का कोई तरीका है?
बार्ट कैलिक्सो

आपके उत्तर के लिए धन्यवाद, यह मुझे ऑटोमैपर के बारे में बहुत कुछ समझने की ओर ले जाता है। बात यह है कि जब आप एपीआई का खुलासा करते हैं, तो आप उन तरीकों से डेटा का सावधानीपूर्वक उपयोग करते हैं, जिनमें कभी-कभी एम्बेडेड मॉडल वांछित नहीं होते हैं क्योंकि आप अपनी 'डोमेन-संबंधित' समस्याओं को एक उपभोक्ता को हस्तांतरित करते हैं और मैं ग्राहकों के लिए बिना नेस्ट प्रकार के उपभोग करना आसान बनाने की कोशिश कर रहा हूं । यदि यह मेरे स्वयं के आंतरिक उपयोग के लिए था, तो मैं सुनिश्चित करने के लिए एम्बेडेड विकल्प के साथ आगे बढ़ूंगा।
बार्ट कैलिक्सो

1
PeoplePhoneDtoआप दिखता अच्छा सुझाव दिया है, लेकिन मैं अब भी लगता है कई स्रोतों से मानचित्रण उपयोगी, सबसे विशेष रूप मानचित्रण दृश्य मॉडल में है। मुझे लगता है कि दृश्य मॉडल के निर्माण के लिए अधिकांश वास्तविक विश्व परिदृश्यों को कई स्रोतों की आवश्यकता होती है। मुझे लगता है कि आप उस मुद्दे के आसपास पाने के लिए चपटा मॉडल नहीं बना सकते हैं, लेकिन मुझे लगता है कि व्यावसायिक स्कीमा जैसा दिखता है, उसे ध्यान दिए बिना व्यू मॉडल बनाना एक अच्छा विचार है।
मफिन मैन

यह भी बताता है कि ऑटोमैपर देखभाल करता है कि टपल में किस प्रकार के क्रम हैं? के Tuple<People, Phone>रूप में ही है Tuple<Phone, People>?
मफिन मैन

2
@ TheMuffinMan Tupleपहले प्रकार के तर्क को उजागर करता है Item1, दूसरे के रूप में Item2, आदि उस अर्थ में, आदेश मायने रखता है।
जोश एम।

1

मैं नीचे के रूप में एक विस्तार विधि लिखूंगा:

    public static TDestination Map<TSource1, TSource2, TDestination>(
        this IMapper mapper, TSource1 source1, TSource2 source2)
    {
        var destination = mapper.Map<TSource1, TDestination>(source1);
        return mapper.Map(source2, destination);
    }

तब उपयोग होगा:

    mapper.Map<People, Phone, PeoplePhoneDto>(people, phone);

1

शायद यह एक पुरानी पोस्ट लगती है, लेकिन हो सकता है कि कुछ लोग अभी भी उसी मुद्दे से जूझ रहे हों, AutoMapper IMapper Map function प्रलेखन का जिक्र करते हुए , हम नए स्रोत से मैपिंग के लिए उसी मौजूदा डेस्टिनेशन ऑब्जेक्ट का फिर से उपयोग कर सकते हैं, बशर्ते कि आपने पहले ही बनाया हो प्रोफ़ाइल में गंतव्य के लिए प्रत्येक स्रोत के लिए मानचित्र, फिर आप इस सरल एक्सटेंशन विधि का उपयोग कर सकते हैं:

कृपया ध्यान दें कि मैंने गंतव्य प्रकार के लिए एक बाधा बनाई है जो कहती है कि यह तत्काल-सक्षम प्रकार होना चाहिए। यदि आपका प्रकार default(TDestination)इसके बजाय उस उपयोग की तरह नहीं है new TDestination()

चेतावनी : इस प्रकार की मैपिंग कभी-कभी थोड़ी खतरनाक होती है क्योंकि गंतव्य मैपिंग गुणों को कई स्रोतों से अधिलेखित किया जा सकता है और समस्या को बड़े ऐप्स में ट्रेस करना सिरदर्द हो सकता है, एक ढीला वर्कआउट है जिसे आप लागू कर सकते हैं, आप नीचे के रूप में कर सकते हैं, लेकिन फिर से यह एक ठोस समाधान नहीं है:


0

यदि आपके पास एक परिदृश्य है जब गंतव्य प्रकार को स्रोतों में से एक से मैप किया जाना चाहिए और आप linq अनुमानों का उपयोग करना चाहते हैं, तो आप निम्नलिखित कर सकते हैं।

    Mapper.CreateMap<People, PeoplePhoneDto>(MemberList.Source);
    Mapper.CreateMap<Phone, PeoplePhoneDto>(MemberList.Source)
          .ForMember(d => d.PhoneNumber, a => a.MapFrom(s => s.Number));

    CreateMap<PeoplePhoneDto,(People,Phone)>(MemberList.Destination)
           .ForMember(x => x.Item1, opts => opts.MapFrom(x => x))
           .ForMember(x => x.Item2, opts => opts.MapFrom(x => x.PhoneNumber))
           .ReverseMap();

मुझे इस तरह से क्रॉस क्रॉस क्वेश्चन के लिए इसकी जरूरत थी।

       var dbQuery =
          from p in _context.People
          from ph in _context.Phones
             .Where(x => ...).Take(1)
          select ValueTuple.Create(p, ph);
       var list = await dbQuery
          .ProjectTo<PeoplePhoneDto>(_mapper.ConfigurationProvider)
          .ToListAsync();

0

बहुत सारे विकल्प पहले से ही प्रदान किए गए हैं, लेकिन उनमें से कोई भी वास्तव में फिट नहीं है जो मैं चाहता था। मैं कल रात सो रहा था और सोचा था:

कहते हैं कि आप अपने दो वर्गों, Peopleऔर Phoneकरने के लिए मैप करना चाहते हैंPeoplePhoneDto

public class People {
   public string FirstName {get;set;}
   public string LastName {get;set;}
}

+

public class Phone {
   public string Number {get;set;}
}

=

public class PeoplePhoneDto {
    public string FirstName {get;set;}
    public string LastName {get;set;}
    public string PhoneNumber {get;set;}
}

आपको वास्तव में ऑटोमैपर उद्देश्यों के लिए एक और आवरण वर्ग की आवश्यकता है।

public class PeoplePhone {
    public People People {get;set;}
    public Phone Phone {get;set;}
}

और फिर मानचित्रण को परिभाषित करें:

CreateMap<PeoplePhone, PeoplePhoneDto>()

और इसका उपयोग करें

var dto = Map<PeoplePhoneDto>(new PeoplePhone
{
    People = people,
    Phone = phone,
});
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.