मैं डैपर के साथ नेस्टेड ऑब्जेक्ट्स की सूची कैसे मैप करूं


127

मैं फिलहाल अपने db एक्सेस के लिए Entity Framework का उपयोग कर रहा हूँ, लेकिन Dapper पर एक नज़र रखना चाहता हूँ। मेरे पास इस तरह की कक्षाएं हैं:

public class Course{
   public string Title{get;set;}
   public IList<Location> Locations {get;set;}
   ...
}

public class Location{
   public string Name {get;set;}
   ...
}

इसलिए कई स्थानों पर एक कोर्स पढ़ाया जा सकता है। एंटिटी फ्रेमवर्क मेरे लिए मैपिंग करता है इसलिए मेरा कोर्स ऑब्जेक्ट स्थानों की एक सूची के साथ आबाद है। मैं इसके बारे में डैपर के साथ कैसे जाऊंगा, क्या यह संभव है या क्या मुझे इसे कई क्वेरी चरणों में करना होगा?


संबंधित प्रश्न: stackoverflow.com/questions/6379155/…
जेरोइन के

यहाँ मेरा समाधान है: stackoverflow.com/a/57395072/8526957
सैम Sch

जवाबों:


57

डैपर एक पूर्ण विकसित ओआरएम नहीं है, यह प्रश्नों और इस तरह की जादुई पीढ़ी को नहीं संभालता है।

आपके विशेष उदाहरण के लिए निम्नलिखित शायद काम करेंगे:

पाठ्यक्रमों को पकड़ो:

var courses = cnn.Query<Course>("select * from Courses where Category = 1 Order by CreationDate");

संबंधित मानचित्रण को पकड़ो:

var mappings = cnn.Query<CourseLocation>(
   "select * from CourseLocations where CourseId in @Ids", 
    new {Ids = courses.Select(c => c.Id).Distinct()});

संबंधित स्थानों को पकड़ो

var locations = cnn.Query<Location>(
   "select * from Locations where Id in @Ids",
   new {Ids = mappings.Select(m => m.LocationId).Distinct()}
);

यह सब नक्शा

पाठक के लिए इसे छोड़कर, आप कुछ मानचित्र बनाते हैं और अपने पाठ्यक्रमों के माध्यम से पुनरावृति करते हैं, जो स्थानों के साथ आबाद होते हैं।

चेतावनीin चाल अगर आप की तुलना में कम है काम करेंगे 2100 लुकअप (SQL सर्वर), अगर आपके पास अधिक आप शायद करने के लिए क्वेरी में संशोधन करना चाहते हैं select * from CourseLocations where CourseId in (select Id from Courses ... )का उपयोग कर जाने यदि यह मामला आप रूप में अच्छी तरह से एक में सभी परिणाम झटका हो सकता हैQueryMultiple


स्पष्टीकरण सैम के लिए धन्यवाद। जैसा कि आपने ऊपर वर्णित किया है, मैं सिर्फ एक दूसरी क्वेरी चला रहा हूं, जो स्थान खोज रही है और मैन्युअल रूप से उन्हें पाठ्यक्रम में नियत कर रही है। मैं बस यह सुनिश्चित करना चाहता था कि मुझे कुछ याद नहीं है जो मुझे एक क्वेरी के साथ करने की अनुमति देगा।
b3n

2
सैम, एक ~ बड़े अनुप्रयोग में जहां संग्रह नियमित रूप से डोमेन ऑब्जेक्ट्स पर उदाहरण के रूप में उजागर होते हैं, आप इस कोड को शारीरिक रूप से स्थित होने की अनुशंसा कहां करेंगे ? (मान लें कि आप अपने कोड में कई अलग-अलग जगहों से एक समान रूप से निर्मित [कोर्स] इकाई का उपभोग करना चाहेंगे ) कंस्ट्रक्टर में? एक वर्ग कारखाने में? कहीं और?
tbone

177

वैकल्पिक रूप से, आप एक खोज के साथ एक क्वेरी का उपयोग कर सकते हैं:

var lookup = new Dictionary<int, Course>();
conn.Query<Course, Location, Course>(@"
    SELECT c.*, l.*
    FROM Course c
    INNER JOIN Location l ON c.LocationId = l.Id                    
    ", (c, l) => {
        Course course;
        if (!lookup.TryGetValue(c.Id, out course))
            lookup.Add(c.Id, course = c);
        if (course.Locations == null) 
            course.Locations = new List<Location>();
        course.Locations.Add(l); /* Add locations to course */
        return course;
     }).AsQueryable();
var resultList = lookup.Values;

यहां देखें https://www.tritac.com/blog/dappernet-by-example/


9
इससे मेरा एक टन बच गया। एक संशोधन जिसकी मुझे आवश्यकता थी कि दूसरों की आवश्यकता हो सकती है इसमें विभाजन को शामिल करना है: तर्क क्योंकि मैं डिफ़ॉल्ट "आईडी" का उपयोग नहीं कर रहा था।
बिल सैमब्रोन

1
LEFT JOIN के लिए आपको लोकेशन लिस्ट में एक नॉच आइटम मिलेगा। उन्हें var आइटम = लुकअप द्वारा निकालें। मद। फ़ोरएच (x => x.ocations.RemoveAll (y => y == null));
चोको स्मिथ

मैं इसे तब तक संकलित नहीं कर सकता जब तक कि मेरे पास पंक्ति 1 के अंत में अर्धविराम न हो और 'AsQueryable ()' से पहले अल्पविराम को हटा दें। मैं उत्तर को संपादित करूंगा, लेकिन 62 upvoters से पहले मुझे लगता है कि यह ठीक है, शायद मुझे कुछ याद आ रहा है ...
बिटकोडर

1
LEFT JOIN के लिए: इस पर एक और Foreach करने की आवश्यकता नहीं है। बस इसे जोड़ने से पहले जांचें: यदि (l! = Null) पाठ्यक्रम।
१२:17 पर jpgrassi

1
चूंकि आप एक शब्दकोश का उपयोग कर रहे हैं। क्या यह तेजी से होगा यदि आपने QueryMultiple और queried कोर्स और स्थान का अलग-अलग उपयोग किया है, तो पाठ्यक्रम में स्थान असाइन करने के लिए एक ही शब्दकोश का उपयोग किया है? यह समान रूप से एक ही चीज है जो आंतरिक जुड़ाव है जिसका अर्थ है कई बाइट्स के रूप में sql ट्रांसफर?
माइक

43

lookupडिक्शनरी की जरूरत नहीं

var coursesWithLocations = 
    conn.Query<Course, Location, Course>(@"
        SELECT c.*, l.*
        FROM Course c
        INNER JOIN Location l ON c.LocationId = l.Id                    
        ", (course, location) => {
            course.Locations = course.Locations ?? new List<Location>();
            course.Locations.Add(location); 
            return course;
        }).AsQueryable();

3
यह उत्कृष्ट है - मेरी राय में यह चयनित उत्तर होना चाहिए। हालांकि, ऐसा करने वाले लोग प्रदर्शन करने के लिए बाहर निकलते हैं, क्योंकि इससे प्रदर्शन प्रभावित हो सकता है।
cr1pto

2
केवल इसके साथ समस्या यह है कि आप प्रत्येक स्थान रिकॉर्ड पर हेडर की नकल कर रहे हैं। यदि प्रति कोर्स कई स्थान हैं, तो यह तार के पार जाने वाले डेटा दोहराव का एक महत्वपूर्ण हिस्सा हो सकता है जो बैंडविड्थ को बढ़ाएगा, पार्स / मैप में अधिक समय लेगा, और उस सभी को पढ़ने के लिए अधिक मेमोरी का उपयोग करेगा।
डैनियल लॉरेंज

10
मुझे यकीन नहीं है कि यह उम्मीद के मुताबिक काम कर रहा है। मेरे पास 3 संबंधित वस्तुओं के साथ 1 मूल वस्तु है। मेरे द्वारा उपयोग की जाने वाली क्वेरी तीन पंक्तियाँ वापस हो जाती है। प्रत्येक पंक्ति के लिए नकल करने वाले माता-पिता का वर्णन करने वाले पहले कॉलम; आईडी पर विभाजन प्रत्येक अद्वितीय बच्चे की पहचान करेगा। मेरे परिणाम 3 बच्चों के साथ 3 डुप्लिकेट माता-पिता हैं .... 3 बच्चों के साथ एक माता-पिता होना चाहिए।
topwik

2
@topwik सही है। यह मेरे लिए उम्मीद के मुताबिक काम नहीं करता है।
1930 को मैकीज Pszczolinski

3
मैं वास्तव में इस कोड के साथ प्रत्येक में 3 माता-पिता, 1 बच्चे के साथ समाप्त हुआ। यह निश्चित नहीं है कि मेरा परिणाम @topwik से अलग क्यों है, लेकिन फिर भी, यह काम नहीं करता है।
th3morg

29

मुझे पता है कि मुझे वास्तव में देर हो चुकी है, लेकिन एक और विकल्प है। आप यहाँ QueryMultiple का उपयोग कर सकते हैं। कुछ इस तरह:

var results = cnn.QueryMultiple(@"
    SELECT * 
      FROM Courses 
     WHERE Category = 1 
  ORDER BY CreationDate
          ; 
    SELECT A.*
          ,B.CourseId 
      FROM Locations A 
INNER JOIN CourseLocations B 
        ON A.LocationId = B.LocationId 
INNER JOIN Course C 
        ON B.CourseId = B.CourseId 
       AND C.Category = 1
");

var courses = results.Read<Course>();
var locations = results.Read<Location>(); //(Location will have that extra CourseId on it for the next part)
foreach (var course in courses) {
   course.Locations = locations.Where(a => a.CourseId == course.CourseId).ToList();
}

3
एक बात ध्यान दें। यदि बहुत सारे स्थान / पाठ्यक्रम हैं, तो आपको एक बार स्थानों के माध्यम से लूप करना चाहिए और उन्हें एक शब्दकोश लुकअप में रखना चाहिए ताकि आपके पास N ^ 2 गति के बजाय एन लॉग एन हो। बड़े डेटासेट में एक बड़ा अंतर बनाता है।
डेनियल लॉरेंज

6

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

string query = @"SELECT c.*, l.*
    FROM Course c
    INNER JOIN Location l ON c.LocationId = l.Id";
using (SqlConnection conn = DB.getConnection())
{
    conn.Open();
    var courseDictionary = new Dictionary<Guid, Course>();
    var list = conn.Query<Course, Location, Course>(
        query,
        (course, location) =>
        {
            if (!courseDictionary.TryGetValue(course.Id, out Course courseEntry))
            {
                courseEntry = course;
                courseEntry.Locations = courseEntry.Locations ?? new List<Location>();
                courseDictionary.Add(courseEntry.Id, courseEntry);
            }

            courseEntry.Locations.Add(location);
            return courseEntry;
        },
        splitOn: "Id")
    .Distinct()
    .ToList();

    return list;
}

4

कुछ छूट रहा है। यदि आप LocationsSQL क्वेरी में प्रत्येक फ़ील्ड को निर्दिष्ट नहीं करते हैं , तो ऑब्जेक्ट Locationभरा नहीं जा सकता है। जरा देखो तो:

var lookup = new Dictionary<int, Course>()
conn.Query<Course, Location, Course>(@"
    SELECT c.*, l.Name, l.otherField, l.secondField
    FROM Course c
    INNER JOIN Location l ON c.LocationId = l.Id                    
    ", (c, l) => {
        Course course;
        if (!lookup.TryGetValue(c.Id, out course)) {
            lookup.Add(c.Id, course = c);
        }
        if (course.Locations == null) 
            course.Locations = new List<Location>();
        course.Locations.Add(a);
        return course;
     },
     ).AsQueryable();
var resultList = lookup.Values;

l.*क्वेरी में उपयोग करते हुए, मेरे पास स्थानों की सूची थी लेकिन डेटा के बिना।


0

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

var lookup = new Dictionary<int, dynamic>();
conn.Query<dynamic, dynamic, dynamic>(@"
    SELECT A.*, B.*
    FROM Client A
    INNER JOIN Instance B ON A.ClientID = B.ClientID                
    ", (A, B) => {
        // If dict has no key, allocate new obj
        // with another level of array
        if (!lookup.ContainsKey(A.ClientID)) {
            lookup[A.ClientID] = new {
                ClientID = A.ClientID,
                ClientName = A.Name,                                        
                Instances = new List<dynamic>()
            };
        }

        // Add each instance                                
        lookup[A.ClientID].Instances.Add(new {
            InstanceName = B.Name,
            BaseURL = B.BaseURL,
            WebAppPath = B.WebAppPath
        });

        return lookup[A.ClientID];
    }, splitOn: "ClientID,InstanceID").AsQueryable();

var resultList = lookup.Values;
return resultList;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.