मैं Dapper.Net में कई क्वेरी के लिए एक कैसे लिखूं?


80

मैंने इस कोड को एक से कई संबंध बनाने के लिए लिखा है, लेकिन यह काम नहीं कर रहा है:

using (var connection = new SqlConnection(connectionString))
{
   connection.Open();

   IEnumerable<Store> stores = connection.Query<Store, IEnumerable<Employee>, Store>
                        (@"Select Stores.Id as StoreId, Stores.Name, 
                                  Employees.Id as EmployeeId, Employees.FirstName,
                                  Employees.LastName, Employees.StoreId 
                           from Store Stores 
                           INNER JOIN Employee Employees ON Stores.Id = Employees.StoreId",
                        (a, s) => { a.Employees = s; return a; }, 
                        splitOn: "EmployeeId");

   foreach (var store in stores)
   {
       Console.WriteLine(store.Name);
   }
}

क्या कोई गलती कर सकता है?

संपादित करें:

ये मेरी संस्थाएँ हैं:

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public double Price { get; set; }
    public IList<Store> Stores { get; set; }

    public Product()
    {
        Stores = new List<Store>();
    }
}

public class Store
{
    public int Id { get; set; }
    public string Name { get; set; }
    public IEnumerable<Product> Products { get; set; }
    public IEnumerable<Employee> Employees { get; set; }

    public Store()
    {
        Products = new List<Product>();
        Employees = new List<Employee>();
    }
}

संपादित करें:

मैं क्वेरी को इसमें बदलता हूं:

IEnumerable<Store> stores = connection.Query<Store, List<Employee>, Store>
        (@"Select Stores.Id as StoreId ,Stores.Name,Employees.Id as EmployeeId,
           Employees.FirstName,Employees.LastName,Employees.StoreId 
           from Store Stores INNER JOIN Employee Employees 
           ON Stores.Id = Employees.StoreId",
         (a, s) => { a.Employees = s; return a; }, splitOn: "EmployeeId");

और मुझे अपवादों से छुटकारा मिल गया है! हालाँकि, कर्मचारियों को मैप नहीं किया जाता है। मुझे अभी भी यकीन नहीं है कि IEnumerable<Employee>पहली क्वेरी में इसके साथ क्या समस्या थी ।


1
आपकी संस्थाएं कैसी दिखती हैं?
गिदोन

2
कैसे काम नहीं कर रहा है? क्या आपको अपवाद मिल रहा है? अप्रत्याशित परिणाम?
driis

1
त्रुटि सार्थक नहीं है इसीलिए मैंने इसे पोस्ट करने की जहमत नहीं उठाई। मुझे मिलता है: "{" मान शून्य नहीं हो सकता। \ r \ n नाम का नाम: कांग्रेस "}"। SqlMapper में त्रुटि फेंकने वाली रेखा है: "il.Emit (OpCodes.Newobj, type.GetConstructor (BindingFlags.Instance | BindingFlags.Public। BindingFlags.Public। BindingFlags.NonPublic, null, Type.EmptyTypes, null)।"
टीसीएम

जवाबों:


162

यह पोस्ट दिखाता है कि अत्यधिक सामान्यीकृत SQL डेटाबेस को कैसे क्वेरी करें , और परिणाम को अत्यधिक नेस्टेड C # POCO ऑब्जेक्ट के सेट में मैप करें।

सामग्री:

  • C # की 8 लाइनें।
  • कुछ यथोचित सरल SQL जो कुछ जोड़ का उपयोग करता है।
  • दो भयानक पुस्तकालय।

अंतर्दृष्टि जो मुझे इस समस्या को हल करने की अनुमति देती है, उससे अलग होना MicroORMहै mapping the result back to the POCO Entities। इस प्रकार, हम दो अलग-अलग पुस्तकालयों का उपयोग करते हैं:

अनिवार्य रूप से, हम डेटाबेस को क्वेरी करने के लिए डैपर का उपयोग करते हैं, फिर परिणाम को सीधे हमारे पीओओ में मैप करने के लिए स्लैपर.एटोमैपर का उपयोग करते हैं।

लाभ

  • सादगी । इसकी 8 से कम लाइनें हैं। मुझे यह समझने, बहस करने और बदलने में बहुत आसान लगता है।
  • कम कोड । कोड की कुछ पंक्तियाँ सभी Slapper.Automapper को आपके द्वारा फेंके गए किसी भी चीज़ को संभालने की आवश्यकता होती हैं, भले ही हमारे पास एक जटिल नेस्टेड POCO हो (यानी POCO जिसमें List<MyClass1>बदले में शामिल हैं List<MySubClass2>, आदि)।
  • गति । इन दोनों पुस्तकालयों के पास अनुकूलन और कैशिंग की एक असाधारण राशि है जो उन्हें लगभग इतनी ही तेजी से चलाते हैं जैसे कि ADO.NET प्रश्न।
  • चिंताओं का पृथक्करण । हम एक अलग के लिए MicroORM को बदल सकते हैं, और मैपिंग अभी भी काम करता है, और इसके विपरीत।
  • लचीलापनSlapper.Automapper मनमाने ढंग से नेस्टेड पदानुक्रमों को संभालता है, यह नेस्टिंग के स्तर के एक जोड़े तक सीमित नहीं है। हम आसानी से तेजी से बदलाव कर सकते हैं, और सब कुछ अभी भी काम करेगा।
  • डिबगिंग । हम पहले देख सकते हैं कि SQL क्वेरी ठीक से काम कर रही है, तो हम जाँच सकते हैं कि SQL क्वेरी परिणाम ठीक से लक्ष्य POCO संगठनों के लिए मैप किया गया है।
  • SQL में विकास में आसानी । मुझे लगता है कि inner joinsफ्लैट परिणामों को वापस करने के साथ चपटा प्रश्न बनाना कई चुनिंदा बयानों को बनाने की तुलना में बहुत आसान है, क्लाइंट साइड पर सिलाई के साथ।
  • एसक्यूएल में अनुकूलित प्रश्नों । एक उच्च सामान्यीकृत डेटाबेस में, एक फ्लैट क्वेरी बनाने से SQL इंजन पूरे में उन्नत अनुकूलन लागू करने की अनुमति देता है जो सामान्य रूप से संभव नहीं होगा यदि कई छोटे व्यक्तिगत प्रश्नों का निर्माण और रन किया गया हो।
  • भरोसा है । डैपर स्टैकऑवरफ्लो के लिए अंतिम छोर है, और, ठीक है, रैंडी बर्डन एक सुपरस्टार का एक सा है। मुझे कोई और कहना चाहिए?
  • विकास की गति। मैं घोंसले के कई स्तरों के साथ कुछ असाधारण जटिल प्रश्न करने में सक्षम था, और देव समय काफी कम था।
  • कम कीड़े। मैंने इसे एक बार लिखा था, यह सिर्फ काम किया, और यह तकनीक अब एक एफटीएसई कंपनी को बिजली देने में मदद कर रही है। इतना कम कोड था कि कोई अप्रत्याशित व्यवहार नहीं था।

नुकसान

  • 1,000,000 पंक्तियों से परे स्केलिंग लौट आई। लौटने पर अच्छी तरह से काम करता है <100,000 पंक्तियों। हालाँकि, अगर हम हमारे और SQL सर्वर के बीच ट्रैफ़िक को कम करने के लिए, 1,000,000 पंक्तियों को वापस ला रहे हैं, तो हमें इसका उपयोग करके इसे समतल नहीं करना चाहिए inner join(जो डुप्लिकेट वापस लाता है), हमें इसके बजाय कई selectस्टेटमेंट्स का उपयोग करना चाहिए और सब कुछ एक साथ वापस करना चाहिए ग्राहक पक्ष (इस पृष्ठ पर अन्य उत्तर देखें)।
  • यह तकनीक क्वेरी ओरिएंटेड है । मैंने डेटाबेस में लिखने के लिए इस तकनीक का उपयोग नहीं किया है, लेकिन मुझे यकीन है कि डैपर कुछ और अतिरिक्त कार्यों के साथ ऐसा करने में सक्षम है, क्योंकि स्टैकओवरफ़्लो स्वयं डैपर को अपने डेटा एक्सेस लेयर (डीएएल) के रूप में उपयोग करता है।

प्रदर्शन का परीक्षण

मेरे परीक्षणों में, Slapper.Automapper ने Dapper द्वारा लौटाए गए परिणामों में एक छोटा ओवरहेड जोड़ा, जिसका अर्थ था कि यह अभी भी एंटिटी फ्रेमवर्क की तुलना में 10x तेज था, और संयोजन अभी भी सैद्धांतिक अधिकतम गति एसक्यूएल # C के करीब सुंदर है

अधिकांश व्यावहारिक मामलों में, अधिकांश ओवरहेड एक कम-से-इष्टतम SQL क्वेरी में होगा, और सी # साइड पर परिणामों के कुछ मानचित्रण के साथ नहीं।

प्रदर्शन परीक्षण के परिणाम

पुनरावृत्तियों की कुल संख्या: 1000

  • Dapper by itself: 1.889 मिलीसेकंड प्रति क्वेरी, का उपयोग करते हुए 3 lines of code to return the dynamic
  • Dapper + Slapper.Automapper: 2.463 मिलीसेकंड प्रति क्वेरी, एक अतिरिक्त का उपयोग करके 3 lines of code for the query + mapping from dynamic to POCO Entities

काम किया उदाहरण

इस उदाहरण में, हमारे पास सूची है Contacts, और प्रत्येक Contactमें एक या अधिक हो सकते हैं phone numbers

POCO एंटिटीज

public class TestContact
{
    public int ContactID { get; set; }
    public string ContactName { get; set; }
    public List<TestPhone> TestPhones { get; set; }
}

public class TestPhone
{
    public int PhoneId { get; set; }
    public int ContactID { get; set; } // foreign key
    public string Number { get; set; }
}

एसक्यूएल टेबल TestContact

यहाँ छवि विवरण दर्ज करें

एसक्यूएल टेबल TestPhone

ध्यान दें कि इस तालिका में एक विदेशी कुंजी है ContactIDजो TestContactतालिका को संदर्भित करती है (यह List<TestPhone>ऊपर POCO में मेल खाती है )।

यहाँ छवि विवरण दर्ज करें

SQL जो फ्लैट परिणाम का उत्पादन करता है

हमारी SQL क्वेरी में, हम उतने ही JOINस्टेटमेंट्स का उपयोग करते हैं, जितने की हमें जरूरत है, एक फ्लैट, डिनॉमिनेटेड फॉर्म में सभी डेटा प्राप्त करने के लिए । हां, यह आउटपुट में डुप्लिकेट का उत्पादन कर सकता है, लेकिन जब हम Slapper.Automapper का उपयोग करते हैं, तो ये डुप्लिकेट स्वचालित रूप से समाप्त हो जाएंगे , इस क्वेरी के परिणाम को सीधे हमारे POCO ऑब्जेक्ट मैप में मैप करने के लिए।

USE [MyDatabase];
    SELECT tc.[ContactID] as ContactID
          ,tc.[ContactName] as ContactName
          ,tp.[PhoneId] AS TestPhones_PhoneId
          ,tp.[ContactId] AS TestPhones_ContactId
          ,tp.[Number] AS TestPhones_Number
          FROM TestContact tc
    INNER JOIN TestPhone tp ON tc.ContactId = tp.ContactId

यहाँ छवि विवरण दर्ज करें

सी # कोड

const string sql = @"SELECT tc.[ContactID] as ContactID
          ,tc.[ContactName] as ContactName
          ,tp.[PhoneId] AS TestPhones_PhoneId
          ,tp.[ContactId] AS TestPhones_ContactId
          ,tp.[Number] AS TestPhones_Number
          FROM TestContact tc
    INNER JOIN TestPhone tp ON tc.ContactId = tp.ContactId";

string connectionString = // -- Insert SQL connection string here.

using (var conn = new SqlConnection(connectionString))
{
    conn.Open();    
    // Can set default database here with conn.ChangeDatabase(...)
    {
        // Step 1: Use Dapper to return the  flat result as a Dynamic.
        dynamic test = conn.Query<dynamic>(sql);

        // Step 2: Use Slapper.Automapper for mapping to the POCO Entities.
        // - IMPORTANT: Let Slapper.Automapper know how to do the mapping;
        //   let it know the primary key for each POCO.
        // - Must also use underscore notation ("_") to name parameters in the SQL query;
        //   see Slapper.Automapper docs.
        Slapper.AutoMapper.Configuration.AddIdentifiers(typeof(TestContact), new List<string> { "ContactID" });
        Slapper.AutoMapper.Configuration.AddIdentifiers(typeof(TestPhone), new List<string> { "PhoneID" });

        var testContact = (Slapper.AutoMapper.MapDynamic<TestContact>(test) as IEnumerable<TestContact>).ToList();      

        foreach (var c in testContact)
        {                               
            foreach (var p in c.TestPhones)
            {
                Console.Write("ContactName: {0}: Phone: {1}\n", c.ContactName, p.Number);   
            }
        }
    }
}

उत्पादन

यहाँ छवि विवरण दर्ज करें

POCO इकाई पदानुक्रम

Visual Studio में देखते हुए, हम देख सकते हैं कि Slapper.Automapper ने हमारे POCO Entities को अच्छी तरह से आबाद किया है, अर्थात हमारे पास एक है List<TestContact>, और प्रत्येक TestContactमें एक है List<TestPhone>

यहाँ छवि विवरण दर्ज करें

टिप्पणियाँ

Dapper और Slapper.Automapper दोनों गति के लिए आंतरिक रूप से सब कुछ कैश करते हैं। यदि आप मेमोरी मुद्दों (बहुत ही असंभावित) में चलते हैं, तो सुनिश्चित करें कि आप कभी-कभी उन दोनों के लिए कैश को साफ़ करें।

सुनिश्चित करें कि आप वापस आने वाले स्तंभों को नाम देते हैं, जो कि Slapper.Automapper सुराग देने के लिए अंडरस्कोर ( _) संकेतन का उपयोग करके POCO संस्थाओं में परिणाम को मैप करते हैं।

सुनिश्चित करें कि आप Slapper.Automapper सुराग प्रत्येक POCO इकाई (लाइनों देखें Slapper.AutoMapper.Configuration.AddIdentifiers) के लिए प्राथमिक कुंजी पर दे । आप इसके Attributesलिए POCO पर भी उपयोग कर सकते हैं । यदि आप इस चरण को छोड़ देते हैं, तो यह गलत हो सकता है (सिद्धांत रूप में), जैसा कि Slapper.Automapper को नहीं पता होगा कि मैपिंग ठीक से कैसे करें।

अद्यतन 2015-06-14

40 से अधिक सामान्यीकृत तालिकाओं के साथ एक विशाल उत्पादन डेटाबेस के लिए इस तकनीक को सफलतापूर्वक लागू किया। यह 16 से अधिक inner joinऔर left joinउचित POCO पदानुक्रम (नेस्टिंग के 4 स्तरों के साथ) में एक उन्नत SQL क्वेरी को मैप करने के लिए पूरी तरह से काम करता है । प्रश्न तेजी से अंधा कर रहे हैं, लगभग उपवास के रूप में ADO.NET में कोडिंग (यह आमतौर पर क्वेरी के लिए 52 मिलीसेकंड था, और POCO पदानुक्रम में फ्लैट परिणाम से मानचित्रण के लिए 50 मिलीसेकंड)। यह वास्तव में क्रांतिकारी कुछ भी नहीं है, लेकिन यह सुनिश्चित करता है कि गति और उपयोग में आसानी के लिए एंटिटी फ्रेमवर्क धड़कता है, खासकर अगर हम जो कर रहे हैं वह सभी प्रश्न चल रहे हैं।

अपडेट 2016-02-19

कोड उत्पादन में 9 महीनों से दोषपूर्ण तरीके से चल रहा है। नवीनतम संस्करण में Slapper.Automapperउन सभी परिवर्तनों को शामिल किया गया है जो मैंने SQL क्वेरी में दिए जा रहे नल से संबंधित समस्या को ठीक करने के लिए लागू किया था।

अद्यतन 2017-02-20

कोड 21 महीनों से उत्पादन में दोषपूर्ण तरीके से चल रहा है, और एक FTSE 250 कंपनी में सैकड़ों उपयोगकर्ताओं से निरंतर प्रश्नों को संभाला है।

Slapper.AutomapperPOCO की सूची में सीधे .csv फ़ाइल की मैपिंग के लिए भी बढ़िया है। IDv की सूची में .csv फ़ाइल पढ़ें, फिर इसे सीधे POCOs की लक्ष्य सूची में मैप करें। एकमात्र चाल यह है कि आपको एक उचित जोड़ना होगा int Id {get; set}, और यह सुनिश्चित करना होगा कि यह प्रत्येक पंक्ति के लिए अद्वितीय है (या फिर ऑटोमेपर पंक्तियों के बीच अंतर करने में सक्षम नहीं होगा)।

अपडेट 2019-01-29

अधिक कोड टिप्पणियाँ जोड़ने के लिए मामूली अद्यतन।

देखें: https://github.com/SlapperAutoMapper/Slapper.AutoMapper


1
मुझे विश्वास है कि आपके सभी वर्ग में तालिका नाम उपसर्ग सम्मेलन पसंद नहीं है, लेकिन यह डैपर के "विभाजन" जैसी किसी चीज़ का समर्थन नहीं करता है?
tbone

3
इस तालिका का नाम सम्मेलन Slapper.Automapper द्वारा आवश्यक है। हां, Dapper के पास सीधे POCOs की मैपिंग के लिए समर्थन है, लेकिन मैं Slapper.Automapper का उपयोग करना पसंद करता हूं क्योंकि कोड इतना साफ और रखरखाव योग्य है।
कंटैंगो

2
मुझे लगता है कि अगर आप अपने सभी कॉलमों को उर्फ ​​नहीं करना चाहते हैं, तो स्लैपर का उपयोग करूंगा - इसके बजाय, आपके उदाहरण में, मैं यह कहने में सक्षम होना चाहता हूं:, स्प्लिटऑन: "फोनआईड" - यह बहुत थोड़ा नहीं होगा सब कुछ उर्फ ​​होने से आसान है?
tbone

1
मैं वास्तव में थप्पड़ मारने वाले की तरह दिखता हूं, बस सोच रहा था कि क्या आपने किसी व्यक्ति से संपर्क नंबर नहीं होने पर बाएं हाथ की कोशिश की है? क्या आपके पास इससे निपटने का अच्छा तरीका है?
प्यार नहीं किया

1
@tbone स्प्लिटऑन में कोई भी जानकारी नहीं होती है कि आपकी वस्तु में यह तत्व कहां है, यही वजह है कि स्लैपर इस तरह से एक पथ का उपयोग करता है
प्यार नहीं किया।

20

मैं इसे यथासंभव सरल रखना चाहता था, मेरा समाधान:

public List<ForumMessage> GetForumMessagesByParentId(int parentId)
{
    var sql = @"
    select d.id_data as Id, d.cd_group As GroupId, d.cd_user as UserId, d.tx_login As Login, 
        d.tx_title As Title, d.tx_message As [Message], d.tx_signature As [Signature], d.nm_views As Views, d.nm_replies As Replies, 
        d.dt_created As CreatedDate, d.dt_lastreply As LastReplyDate, d.dt_edited As EditedDate, d.tx_key As [Key]
    from 
        t_data d
    where d.cd_data = @DataId order by id_data asc;

    select d.id_data As DataId, di.id_data_image As DataImageId, di.cd_image As ImageId, i.fl_local As IsLocal
    from 
        t_data d
        inner join T_data_image di on d.id_data = di.cd_data
        inner join T_image i on di.cd_image = i.id_image 
    where d.id_data = @DataId and di.fl_deleted = 0 order by d.id_data asc;";

    var mapper = _conn.QueryMultiple(sql, new { DataId = parentId });
    var messages = mapper.Read<ForumMessage>().ToDictionary(k => k.Id, v => v);
    var images = mapper.Read<ForumMessageImage>().ToList();

    foreach(var imageGroup in images.GroupBy(g => g.DataId))
    {
        messages[imageGroup.Key].Images = imageGroup.ToList();
    }

    return messages.Values.ToList();
}

मैं अभी भी डेटाबेस में एक कॉल करता हूं, और जब मैं अब एक के बजाय 2 प्रश्नों को निष्पादित करता हूं, तो दूसरी क्वेरी कम इष्टतम LEFT जॉइन के बजाय INNER जॉइन का उपयोग कर रही है।


5
मुझे यह तरीका पसंद है। शुद्ध डापर और IMHO अधिक समझने योग्य मानचित्रण।
अवनर

1
इस तरह लगता है कि एक विस्तार विधि में रखना आसान होगा जो एक जोड़े को अल्मबेडास लेता है, एक कुंजी चयनकर्ता के लिए और एक बच्चे के चयनकर्ता के लिए। इसी तरह .Join(लेकिन चपटा परिणाम के बजाय एक वस्तु ग्राफ बनाता है।
एरोनल्स

8

एंड्रयू के जवाब का एक मामूली संशोधन जो मूल कुंजी का चयन करने के लिए एक Func का उपयोग करता है GetHashCode

public static IEnumerable<TParent> QueryParentChild<TParent, TChild, TParentKey>(
    this IDbConnection connection,
    string sql,
    Func<TParent, TParentKey> parentKeySelector,
    Func<TParent, IList<TChild>> childSelector,
    dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
{
    Dictionary<TParentKey, TParent> cache = new Dictionary<TParentKey, TParent>();

    connection.Query<TParent, TChild, TParent>(
        sql,
        (parent, child) =>
            {
                if (!cache.ContainsKey(parentKeySelector(parent)))
                {
                    cache.Add(parentKeySelector(parent), parent);
                }

                TParent cachedParent = cache[parentKeySelector(parent)];
                IList<TChild> children = childSelector(cachedParent);
                children.Add(child);
                return cachedParent;
            },
        param as object, transaction, buffered, splitOn, commandTimeout, commandType);

    return cache.Values;
}

उदाहरण उपयोग

conn.QueryParentChild<Product, Store, int>("sql here", prod => prod.Id, prod => prod.Stores)

इस समाधान के साथ ध्यान देने वाली एक बात, आपका अभिभावक वर्ग बाल सम्पत्ति की तात्कालिकता के लिए जिम्मेदार है। class Parent { public List<Child> Children { get; set; } public Parent() { this.Children = new List<Child>(); } }
क्ले

1
यह समाधान उत्कृष्ट है और हमारे लिए काम किया है। मुझे बच्चों के साथ एक चेक में जोड़ना था। यदि कोई बाल पंक्तियाँ वापस नहीं आतीं तो अशक्त होने की जाँच करें।
tlbignerd

7

इस उत्तर के अनुसार Dapper.Net में निर्मित कई मैपिंग सहायता के लिए कोई नहीं है। क्वेरी हमेशा डेटाबेस पंक्ति के अनुसार एक ऑब्जेक्ट लौटाएगी। एक वैकल्पिक समाधान शामिल है, हालांकि।


मुझे खेद है लेकिन मुझे समझ नहीं आ रहा है कि मैं अपनी क्वेरी में इसका उपयोग कैसे करूँ? यह डेटाबेस को बिना जोड़ के 2 बार क्वेरी करने की कोशिश कर रहा है (और उदाहरण में हार्डकोड 1 का उपयोग करके)। उदाहरण में केवल 1 मुख्य इकाई को लौटाया जा रहा है जिसमें बदले में बाल संस्थाएँ हैं। मेरे मामले में मैं शामिल होने का प्रोजेक्ट करना चाहता हूं (सूची जिसमें आंतरिक रूप से सूची शामिल है)। मैं आपके द्वारा उल्लिखित लिंक के साथ कैसे करूँ? लिंक में जहां रेखा कहती है: (contact, phones) => { contact.Phones = phones; } मुझे उन फोन के लिए एक फ़िल्टर लिखना होगा, जिनके संपर्क में संपर्क के संपर्क से मेल खाता हो। यह काफी अक्षम है।
टीसीएम

@ एंथनी माइक के जवाब पर एक नज़र डालें। वह दो परिणाम सेटों के साथ एक एकल क्वेरी निष्पादित करता है और बाद में मानचित्र विधि के साथ उन्हें जोड़ता है। बेशक, आपको अपने मामले में मूल्य को हार्डकोड करने की आवश्यकता नहीं है। मैं कुछ घंटों में एक उदाहरण प्रस्तुत करने की कोशिश करूँगा।
दामिर अरह

1
ठीक है, मैं इसे अंत में काम कर रहा हूँ। धन्यवाद! पता नहीं कि यह डेटाबेस को 2 बार क्वेरी करने के प्रदर्शन को कैसे प्रभावित करेगा, जो कि किसी एकल जॉइन का उपयोग करके पूरा किया जा सकता है।
TCM

2
इसके अलावा मुझे समझ में नहीं आता है कि 3 तालिकाओं: पी
टीसीएम

1
यह पूरी तरह से बेकार है .. पृथ्वी पर जुड़ने से क्यों बचें?
गोरिल्लाएप

2

यहां क्रूड वर्कअराउंड है

    public static IEnumerable<TOne> Query<TOne, TMany>(this IDbConnection cnn, string sql, Func<TOne, IList<TMany>> property, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
    {
        var cache = new Dictionary<int, TOne>();
        cnn.Query<TOne, TMany, TOne>(sql, (one, many) =>
                                            {
                                                if (!cache.ContainsKey(one.GetHashCode()))
                                                    cache.Add(one.GetHashCode(), one);

                                                var localOne = cache[one.GetHashCode()];
                                                var list = property(localOne);
                                                list.Add(many);
                                                return localOne;
                                            }, param as object, transaction, buffered, splitOn, commandTimeout, commandType);
        return cache.Values;
    }

इसका कोई मतलब नहीं है सबसे कुशल तरीका है, लेकिन यह आपको ऊपर और चलाने मिल जाएगा। जब मुझे मौका मिलेगा, मैं इसे आज़माऊँगा और अनुकूलित करूँगा।

इसे इस तरह उपयोग करें:

conn.Query<Product, Store>("sql here", prod => prod.Stores);

ध्यान रखें कि आपकी वस्तुओं को लागू करने की आवश्यकता है GetHashCode, शायद इस तरह:

    public override int GetHashCode()
    {
        return this.Id.GetHashCode();
    }

11
कैश कार्यान्वयन त्रुटिपूर्ण है। हैश कोड अद्वितीय नहीं हैं - दो वस्तुओं में एक ही हैश कोड हो सकता है। इससे वस्तुओं की सूची उन वस्तुओं से भरी हो सकती है जो किसी अन्य वस्तु से संबंधित हैं ..
21

2

यहाँ एक और तरीका है:

ऑर्डर (एक) - ऑर्डरडेटेल (कई)

using (var connection = new SqlCeConnection(connectionString))
{           
    var orderDictionary = new Dictionary<int, Order>();

    var list = connection.Query<Order, OrderDetail, Order>(
        sql,
        (order, orderDetail) =>
        {
            Order orderEntry;

            if (!orderDictionary.TryGetValue(order.OrderID, out orderEntry))
            {
                orderEntry = order;
                orderEntry.OrderDetails = new List<OrderDetail>();
                orderDictionary.Add(orderEntry.OrderID, orderEntry);
            }

            orderEntry.OrderDetails.Add(orderDetail);
            return orderEntry;
        },
        splitOn: "OrderDetailID")
    .Distinct()
    .ToList();
}

स्रोत : http://dapper-tutorial.net/result-multi-mapping#example---query-multi-mapping-one-to-many

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