query example c# linq data-structures linq-to-objects

c# - example - Linq-SelectMany Confusion



select linq c# example (4)

Aquí está su consulta usando SelectMany , modelado exactamente después de su ejemplo. ¡Mismo resultado!

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

El primer argumento asigna a cada cliente una colección de órdenes (completamente análogas a la cláusula ''where'' que ya tiene).

El segundo argumento transforma cada par coincidente {(c1, o1), (c1, o2) .. (c3, o9)} en un nuevo tipo, que he hecho igual que en su ejemplo.

Asi que:

  • arg1 correlaciona cada elemento de la colección base con otra colección.
  • arg2 (opcional) transforma cada par en un nuevo tipo

La colección resultante es plana como esperarías en tu ejemplo original.

Si tuviera que omitir el segundo argumento, terminaría con una colección de todas las órdenes que coinciden con un cliente. Sería solo eso, una colección plana de objetos Order .

Usarlo lleva mucho tiempo acostumbrándome, a veces todavía me cuesta entenderlo. :(

Según lo que entiendo de la documentación de SelectMany, se podría usar para producir una secuencia (aplanada) de una relación 1-many.

Tengo las siguientes clases

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; } }

Luego trato de usarlos usando la sintaxis de la expresión de consulta como tal

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);

Esto da a lo que necesito.

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

Supongo que esto se traduce en el uso del método SelectMany cuando no se utiliza la sintaxis de la expresión de consulta.

De cualquier manera, estoy tratando de entender todo usando SelectMany. Entonces, incluso si mi consulta anterior no se traduce a SelectMany, dadas las dos clases y los datos simulados, ¿podría alguien proporcionarme una consulta de linq que use SelectMany?


Aquí hay otra opción usando SelectMany

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

Si usa Entity Framework o LINQ to Sql y tiene una asociación (relación) entre las entidades, puede hacerlo:

var customerOrders = customers.SelectMany( c => c.orders .Select(p => new {CustomerId = c.Id, OrderDescription = p.Description}));


Aunque esta es una vieja pregunta, pensé que mejoraría un poco las excelentes respuestas:

SelectMany devuelve una lista (que puede estar vacía) para cada elemento de la lista de control. Cada elemento en estas listas de resultados se enumera en la secuencia de salida de las expresiones y, por lo tanto, se concatenan en el resultado. Por lo tanto, una lista ''lista -> b'' lista [] -> concatenar -> b ''.

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); } } }


SelectMany () funciona como Seleccionar, pero con esa característica adicional de aplanar una colección que está seleccionada. Debe usarse siempre que desee una proyección de elementos de subcolecciones, y no le importa el elemento que contiene la subcolección.

Por ejemplo, digamos que su dominio se veía así:

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; } }

Para obtener la misma lista que quería, su Linq se vería así:

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

... que producirá el mismo resultado sin necesidad de la colección plana de pedidos. SelectMany toma cada colección de órdenes del cliente y las itera para producir un IEnumerable<Order> IEnumerable<Customer> un IEnumerable<Customer> .