Linq-SelectMany
Z tego, co rozumiem z dokumentacji SelectMany, można go użyć do wytworzenia (spłaszczonej) sekwencji relacji 1-many.
Mam następujące klasy
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; }
}
Następnie staram się ich używać używając składni wyrażenia zapytania tak jak
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);
To daje to, czego potrzebuję.
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
Zakładam, że to przekłada się na użycie metody SelectMany, gdy nie używa składni wyrażenia zapytania?
/ Align = "left" / SelectMany. Więc nawet jeśli moje powyższe zapytanie nie przekłada się na SelectMany, biorąc pod uwagę dwie klasy i dane makiety, czy ktoś mógłby mi dostarczyć zapytanie linq, które używa SelectMany?4 answers
Oto twoje zapytanie za pomocą SelectMany
, wzorowane dokładnie na twoim przykładzie. To samo wyjście!
var customerOrders2 = customers.SelectMany(
c => orders.Where(o => o.CustomerId == c.Id),
(c, o) => new { CustomerId = c.Id, OrderDescription = o.Description });
Pierwszy argument mapuje każdego klienta do zbioru zleceń (całkowicie analogicznie do klauzuli "gdzie", którą już posiadasz).
Drugi argument przekształca każdą dopasowaną parę {(c1, o1), (c1, o2).. w nowy typ, który zrobiłem tak samo jak twój przykład.
Więc:
- arg1 mapuje każdy element w zbiorze bazowym na inny kolekcja.
- arg2 (opcjonalnie) przekształca każdą parę w nowy typ
Wynikowa kolekcja jest płaska, jak można się spodziewać w oryginalnym przykładzie.
Jeśli miałbyś pominąć drugi argument, skończyłbyś z zbiorem wszystkich zamówień dopasowanych do klienta. Byłby to po prostu płaski zbiór Order
obiektów.
Używanie go wymaga dużo przyzwyczajenia, nadal mam problemy z owijaniem głowy wokół niego czasami. :(
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-16 15:42:10
SelectMany () działa jak Select, ale z tą dodatkową funkcją spłaszczania wybranej kolekcji. Powinien być używany, gdy chcemy rzutować elementy podzbiorów i nie dbamy o element zawierający podzbiory.
Na przykład, powiedzmy, że Twoja domena wyglądała tak:
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; }
}
Aby uzyskać tę samą listę, którą chciałeś, Twój Linq wyglądałby mniej więcej tak:
var customerOrders = Customers
.SelectMany(c=>c.Orders)
.Select(o=> new { CustomerId = o.Customer.Id,
OrderDescription = o.Description });
... które przyniosą ten sam efekt bez konieczności płaskiego zbieranie zamówień. SelectMany pobiera kolekcję zamówień każdego klienta i powtarza ją, aby wytworzyć IEnumerable<Order>
z IEnumerable<Customer>
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-16 15:38:00
Choć to stare pytanie, pomyślałem, że poprawię trochę doskonałe odpowiedzi:
SelectMany zwraca listę (która może być pusta) dla każdego elementu listy kontrolnej. Każdy element w tych listach wyników jest wyliczany do sekwencji wyjściowej wyrażeń, a więc jest łączony do wyniku. Stąd, a 'list - > B' list [] - > concatenate - > B' list.
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);
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-13 05:43:01
Oto kolejna opcja za pomocą SelectMany
var customerOrders = customers.SelectMany(
c => orders.Where(o => o.CustomerId == c.Id)
.Select(p => new {CustomerId = c.Id, OrderDescription = p.Description}));
Jeśli używasz encji Framework lub LINQ do Sql i masz związek (relację) między encjami, możesz to zrobić:
var customerOrders = customers.SelectMany(
c => c.orders
.Select(p => new {CustomerId = c.Id, OrderDescription = p.Description}));
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-16 09:26:13