2011-02-16 13 views
75

SelectMany के प्रलेखन से जो मैं समझता हूं उससे, मैं इसे 1-कई रिश्तों के एक (फ़्लैटेड) अनुक्रम का उत्पादन करने के लिए उपयोग कर सकता हूं।लिंक - SelectMany भ्रम

मैं निम्नलिखित वर्गों

public class Customer 
    { 
    public int Id { get; set; } 
    public string Name { get; set; } 
    } 

    class Order 
    { 
    public int Id { get; set; } 
    public int CustomerId { get; set; } 
    public string Description { get; set; } 
    } 

मैं तो जैसे इतना

var customers = new Customer[] 
    { 
    new Customer() { Id=1, Name ="A"}, 
    new Customer() { Id=2, Name ="B"}, 
    new Customer() { Id=3, Name ="C"} 
    }; 

    var orders = new Order[] 
    { 
    new Order { Id=1, CustomerId=1, Description="Order 1"}, 
    new Order { Id=2, CustomerId=1, Description="Order 2"}, 
    new Order { Id=3, CustomerId=1, Description="Order 3"}, 
    new Order { Id=4, CustomerId=1, Description="Order 4"}, 
    new Order { Id=5, CustomerId=2, Description="Order 5"}, 
    new Order { Id=6, CustomerId=2, Description="Order 6"}, 
    new Order { Id=7, CustomerId=3, Description="Order 7"}, 
    new Order { Id=8, CustomerId=3, Description="Order 8"}, 
    new Order { Id=9, CustomerId=3, Description="Order 9"} 
    }; 

    var customerOrders = from c in customers 
         from o in orders 
         where o.CustomerId == c.Id 
         select new 
           { 
           CustomerId = c.Id 
           , OrderDescription = o.Description 
           }; 

    foreach (var item in customerOrders) 
    Console.WriteLine(item.CustomerId + ": " + item.OrderDescription); 

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

1: Order 1 
1: Order 2 
1: Order 3 
1: Order 4 
2: Order 5 
2: Order 6 
3: Order 7 
3: Order 8 
3: Order 9 

मैं इस SelectMany विधि जब क्वेरी अभिव्यक्ति सिंटैक्स का उपयोग नहीं करने के लिए अनुवाद का उपयोग कर मान?

किसी भी तरह से, मैं SelectMany का उपयोग करके अपने सिर को लपेटने की कोशिश कर रहा हूं। तो अगर मेरी उपरोक्त क्वेरी दो श्रेणियों और नकली डेटा को देखते हुए SelectMany में अनुवाद नहीं करती है, तो क्या कोई मुझे एक linq क्वेरी प्रदान कर सकता है जो SelectMany का उपयोग करता है?

+3

सी [भाग 41] (http://msmvps.com/sklogs/jon_skeet/archive/2011/01/28/reimplementing-linq-to-objects-part-41-how-query-expressions-work.aspx) जॉन स्कीट की [एडुलिनक श्रृंखला] (http://msmvps.com /blogs/jon_skeet/archive/tags/Edulinq/default.aspx)। यह क्वेरी अभिव्यक्ति अनुवाद प्रक्रिया बताता है। –

+2

इसके बारे में सोचकर, यह भी देखें [भाग 9: SelectMany] (http://msmvps.com/blogs/jon_skeet/archive/2010/12/27/reimplementing-linq-to-objects-part-9-selectmany.aspx) :) –

+3

जॉन स्कीट की एडुलिनक श्रृंखला अब उपलब्ध है [यहां] (http://codeblog.jonskeet.uk/category/edulinq/)। –

उत्तर

96

SelectMany का उपयोग करके आपकी क्वेरी यहां दी गई है, जो आपके उदाहरण के ठीक बाद मॉडलिंग की गई है। वही आउटपुट!

 var customerOrders2 = customers.SelectMany(
      c => orders.Where(o => o.CustomerId == c.Id), 
      (c, o) => new { CustomerId = c.Id, OrderDescription = o.Description }); 

पहला तर्क आदेश का एक संग्रह (पूरी तरह से 'जहां' खंड आपके पास पहले से analagous) करने के लिए प्रत्येक ग्राहक मैप करता है।

दूसरा तर्क प्रत्येक मिलान की जोड़ी {(सी 1, ओ 1), (सी 1, ओ 2) .. (सी 3, ओ 9)} को एक नए प्रकार में बदल देता है, जिसे मैंने आपके उदाहरण के समान बनाया है।

तो:

  • ARG1 दूसरे संग्रह में आधार संग्रह में प्रत्येक तत्व मैप करता है।
  • ARG2 (वैकल्पिक) एक नए प्रकार

जिसके परिणामस्वरूप संग्रह सपाट है जैसे आप अपने मूल उदाहरण में उम्मीद थी में प्रत्येक जोड़ी बदल देती है।

यदि आप दूसरे तर्क को छोड़ना चाहते थे, तो आप ग्राहक को मैच के सभी आदेशों के संग्रह के साथ समाप्त हो जाएंगे। यह बस इतना होगा, Order वस्तुओं का एक फ्लैट संग्रह।

इसका उपयोग करने में बहुत अधिक उपयोग होता है, मुझे अभी भी कभी-कभी मेरे सिर को लपेटने में परेशानी होती है। इन में

SelectMany नियंत्रित सूची के प्रत्येक तत्व के लिए एक सूची (खाली हो सकता है) रिटर्न प्रत्येक तत्व: :(

+2

आपके उत्तर और स्पष्टीकरण के लिए धन्यवाद। यही वही है जो मुझे चाहिए था। मेरे प्रश्न के संदर्भ में पूरी तरह उत्तर देने के लिए भी धन्यवाद, यह समझने में इतना आसान बनाता है। –

+0

पीट की खातिर, क्यों डालें() * अंदर * SelectMany() मुझे इतने लंबे समय तक elude ?? यह इंगित करने के लिए धन्यवाद ... –

+0

सिर्फ रिकॉर्ड के लिए, 'ग्रुपबी' इस विशेष परिदृश्य के लिए एक बेहतर विकल्प हो सकता है। – Ekevoo

26

SelectMany() चयन की तरह काम करता है, लेकिन चयनित संग्रह को फ़्लैटन करने की उस अतिरिक्त सुविधा के साथ। जब भी आप उप-संग्रह के तत्वों का प्रक्षेपण चाहते हैं, तब इसका उपयोग किया जाना चाहिए, और उप-संग्रह के तत्व के बारे में परवाह नहीं है।

उदाहरण के लिए, मान लें कि आपके डोमेन इस तरह देखा:

public class Customer 
    { 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public List<Order> Orders { get; set; } 
    } 

    class Order 
    { 
    public int Id { get; set; } 
    public Customer Customer { get; set; } 
    public string Description { get; set; } 
    } 

एक ही सूची आप चाहते थे के लिए, अपना Linq कुछ इस तरह दिखेगा:

var customerOrders = Customers 
         .SelectMany(c=>c.Orders) 
         .Select(o=> new { CustomerId = o.Customer.Id, 
              OrderDescription = o.Description }); 

... जो होगा ऑर्डर के फ्लैट संग्रह की आवश्यकता के बिना एक ही परिणाम उत्पन्न करें। SelectMany प्रत्येक ग्राहक के ऑर्डर संग्रह को लेता है और IEnumerable<Order> को IEnumerable<Customer> से उत्पन्न करने के लिए इसके माध्यम से पुन: प्रयास करता है।

+2

* "(...) और उप-संग्रह के तत्व के बारे में परवाह नहीं है।" * यदि आप फ़्लैटनिंग चाहते हैं, और आप युक्त तत्व के बारे में परवाह करते हैं, तो [selectMany का अधिभार] है (http://msdn.microsoft.com/en-us/library/bb534631.aspx) इसके लिए :) –

+0

@Keith आपके उत्तर के लिए धन्यवाद। मैं आदेशों के एक फ्लैट संग्रह के साथ इसका उपयोग कैसे करूं? –

+0

आपका डोमेन थोड़ा संदिग्ध दिखता है। एक आदेश में एक ग्राहक होता है जिसमें बदले में कई ऑर्डर होते हैं? –

5

हालांकि इस एक पुराने सवाल है, मैंने सोचा कि मैं उत्कृष्ट जवाब एक छोटे से सुधार होगा। परिणाम सूचियों भाव में enumerated हैं 'उत्पादन अनुक्रम और इतने परिणाम में concatenated रहे हैं इसलिए, एक।' सूची -> ख 'की सूची [] -> CONCATENATE -> ख' की सूची

using System; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 
using System.Linq; 
using System.Diagnostics; 
namespace Nop.Plugin.Misc.WebServices.Test 
{ 
    [TestClass] 
    public class TestBase 
    { 
     [TestMethod] 
     public void TestMethod1() 
     { //See result in TestExplorer - test output 
      var a = new int[]{7,8}; 
      var b = new int[] 
        {12,23,343,6464,232,75676,213,1232,544,86,97867,43}; 
      Func<int, int, bool> numberHasDigit = 
        (number 
        , digit) => 
         (number.ToString().Contains(digit.ToString())); 

      Debug.WriteLine("Unfiltered: All elements of 'b' for each element of 'a'"); 
      foreach(var l in a.SelectMany(aa => b)) 
       Debug.WriteLine(l); 
      Debug.WriteLine(string.Empty); 
      Debug.WriteLine("Filtered:" + 
      "All elements of 'b' for each element of 'a' filtered by the 'a' element"); 
      foreach(var l in a.SelectMany(aa => b.Where(bb => numberHasDigit(bb, aa)))) 
       Debug.WriteLine(l); 
     } 
    } 
} 
0

यहाँ एक और विकल्प है। SelectMany

var customerOrders = customers.SelectMany(
    c => orders.Where(o => o.CustomerId == c.Id) 
    .Select(p => new {CustomerId = c.Id, OrderDescription = p.Description})); 
का उपयोग करना

आप इकाई की रूपरेखा या SQL करने के लिए LINQ का उपयोग करें और आप संस्थाओं के बीच एक संघ (संबंध) है, तो आप ऐसा कर सकते:

var customerOrders = customers.SelectMany(
    c => c.orders 
    .Select(p => new {CustomerId = c.Id, OrderDescription = p.Description})); 
संबंधित मुद्दे