Capitulo 3: Introducción a LINQ (Parte 3)

Lección 2: Uso de consultas LINQ

Sintaxis y métodos básicos de consultas

Para consultas básicas, el uso de LINQ en Visual Basic o C # es muy fácil e intuitivo ya que ambos lenguajes proporcionan palabras clave que se asignan directamente a las funciones que se han agregado a través de métodos de extensión. La ventaja es que se pueden escribir consultas escribir de una manera muy similar a SQL, obtener el apoyo IntelliSense en todo momento.

Ejemplo: el siguiente horario contiene una lista de los días que están ocupados, y desea saber si está ocupado en un día específico.

--------------------------------------VB------------------------------------------

Private Function GetDates() As List(Of DateTime)

Return New List(Of DateTime) From

{

New DateTime(11, 1, 1),

New DateTime(11, 2, 5),

New DateTime(11, 1, 3),

New DateTime(11, 10, 14),

New DateTime(11, 1, 4)

}

End Function

Dim schedule = GetDates()

Dim areYouAvailable = new DateTime(11, 7, 10)

Dim busy = From d In schedule

Where d = areYouAvailable

Select d

For Each busyDate In busy

txtLog.WriteLine("Lo siento, pero estoy ocupada el {0:MM/dd/yy}", busyDate)

Next

--------------------------------------CS------------------------------------------

private List<DateTime> GetDates()

{

return new List<DateTime>

{

new DateTime(11, 1, 1),

new DateTime(11, 2, 5),

new DateTime(11, 1, 3),

new DateTime(11, 10, 14),

new DateTime(11, 1, 4)

};

}

private void basicLINQToolStripMenuItem_Click(object sender, EventArgs e)

{

var schedule = GetDates();

var areYouAvailable = new DateTime(11,7, 5);

var busy = from d in schedule

where d == areYouAvailable

select d;

foreach(var busyDate in busy)

{

txtLog.WriteLine("Lo siento, pero estoy ocupada el {0:MM/dd/yy}", busyDate);

}

}

El código se podría hacer mas simple, utilizando el metodo where y la consulta queradia de la siguiente forma:

--------------------------------------VB------------------------------------------

Dim schedule = GetDates()

Dim areYouAvailable = New DateTime(11,7,5)

For Each busyDate In schedule.Where(Function(d) d = areYouAvailable)

txtLog.WriteLine("Lo siento, pero estoy ocupada el {0:MM/dd/yy}", busyDate)

Next

--------------------------------------CS------------------------------------------

var schedule = GetDates();

var areYouAvailable = new DateTime(11,7,5);

foreach (var busyDate in schedule.Where(d=>d==areYouAvailable))

{

txtLog.WriteLine("Lo siento, pero estoy ocupada el {0:MM/dd/yy}", busyDate);

}

Palabras clave de LINQ
A continuación se muestra una lista de palabras clave disponibles en LinQ, con una pequeña descripción de cada una:

Cláusula

Descripción

from

Especifica un origen de datos y una variable de rango (similar a una variable de iteración).

where

Filtra elementos de origen basándose en una o varias expresiones Boolean separadas por operadores lógicos AND y OR (&& o ||).

select

Especifica el tipo y la forma que tendrán los elementos en la secuencia devuelta cuando se ejecute la consulta.

group

Agrupa los resultados de la consulta según un valor de clave especificado.

into

Proporciona un identificador que puede actuar como referencia a los resultados de una cláusula join, group o select.

orderby

Ordena los resultados de la consulta en orden ascendente o descendente basándose en el comparador predeterminado para el tipo de elemento.

join

Combina dos orígenes de datos basándose en una comparación de igualdad entre dos criterios de coincidencia especificados.

let

Presenta una variable de rango para almacenar los resultados de una subexpresión en una expresión de consulta.

in

Palabra clave contextual en una cláusula join.

on

Palabra clave contextual en una cláusula join.

equals

Palabra clave contextual en una cláusula join.

by

Palabra clave contextual en una cláusula group.

ascending

Palabra clave contextual en una cláusula orderby.

descending

Palabra clave contextual en una cláusula orderby.

 

Además de las palabras clave que aparecen en la tabla anterior, Visual Basic proporciona palabras clave que C # no implementa.

Cláusula

Descripción

Distinct

filtra elementos duplicados

Skip/Skip While

Salta por encima de algunos elementos antes de devolver los resultados

Take/Take While

limita cuantos elementos se deben retornar

Aggregate

Incluye funciones de agregado en las consultas

All

determina si todos los elementos cumplen con el criterio especificado

Any

si alguno de los elementos cumple con el criterio especificado

Average

palabra clave contextual en la cláusula Aggregate que calcula el valor promedio

Count

número de elementos que cumplen con el criterio especificado

Group

Palabra clave contextual en la cláusula Aggregate que proporciona acceso a los resultados de un grupo o a la cláusula  group join.

LongCount

número de elementos que cumplen con el criterio especificado (en long)

Max

Palabra clave contextual en la cláusula Aggregate que proporciona el valor máximo

Min

Palabra clave contextual en la cláusula Aggregate que proporciona el valor mínimo

Sum

Palabra clave contextual en la cláusula Aggregate que proporciona la suma de los elementos

Todos los métodos de extensión consulta están disponibles en ambos idiomas, incluso si no hay un mapeo de lenguaje palabra clave para el método de extensión de la consulta.

Proyecciones
Permiten transformar la salida de la consulta de LINQ usando tipo anonymouns.

Usando la palabra clave Let para ayudar a las proyecciones
Puede utilizar la palabra clave let para crear una variable temporal dentro de la consulta de LINQ. Let es como una variante de la palabra clave SELECT utilizada en la consulta:

--------------------------------------VB------------------------------------------

Dim cars = GetCars()

Dim vinsAndMakes = From c In cars

Let makeModel = c.Make & " " & c.Model

Where makeModel.Contains("B")

Select New With

{

c.VIN,

.MakeModel = makeModel

}

For Each item In vinsAndMakes

txtLog.WriteLine("VIN:{0} Make and Model:{1}", item.VIN, item.MakeModel)

Next

--------------------------------------CS------------------------------------------

var cars = GetCars();

var vinsAndMakes = from c in cars

let makeModel = c.Make + " " + c.Model

where makeModel.Contains('B')

select new { c.VIN, MakeModel=makeModel };

foreach (var item in vinsAndMakes)

{

txtLog.WriteLine("VIN:{0} Make and Model:{1}", item.VIN, item.MakeModel);

}

Resultado:

VIN:DEF123 Make and Model:BMW Z-3
VIN:HIJ123 Make and Model:VW Bug

Especificando un filtro
C # y Visual Basic tienen palabras clave que se corresponden directamente con el método de extensión en la consulta. Se puede especificar un predicado (expresión que se evalúa como un valor booleano) para determinar los elementos que se devuelven, como vimos en el ejemplo anterior.

Especificando un Tipo de ordenación
La palabra clave orderby permite ordenar ascendente o descendente. Además, se puede ordenar en varias propiedades para realizar una ordenación compuesta.

--------------------------------------VB------------------------------------------

Dim cars = GetCars()

Dim sorted = From c In cars

Order By c.Make Ascending, c.Model Descending

Select c

For Each myCar In sorted

txtLog.WriteLine("Car VIN:{0}, Make:{1}, Model:{2} Year:{3}",

myCar.VIN, myCar.Make, myCar.Model, myCar.Year)

Next

End Sub

--------------------------------------CS------------------------------------------

var cars = GetCars();

var sorted = from c in cars

orderby c.Make ascending, c.Model descending

select c;

foreach (var myCar in sorted)

{

txtLog.WriteLine("Car VIN:{0}, Make:{1}, Model:{2} Year:{3}",

myCar.VIN, myCar.Make, myCar.Model, myCar.Year);

}

El resultado seria:

Car VIN:ABC456, Make:Audi, Model:TT Year:2008
Car VIN:DEF123, Make:BMW, Model:Z-3 Year:2005
Car VIN:ABC123, Make:Ford, Model:F-250 Year:2000
Car VIN:DEF456, Make:Ford, Model:F-150 Year:1998
Car VIN:HIJ123, Make:VW, Model:Bug Year:1956

Paginación
La capacidad de observar los datos de una página a la vez es siempre un requisito cuando una gran cantidad de datos ha sido recuperado. Visual Basic ofrece los siguientes métodos de extensión como palabras clave.

--------------------------------------VB------------------------------------------

Dim pageSize = 10

'se crea 5 copias  del arreglo cars - total 25 filas

Dim cars = Enumerable.Range(1, 5) _

.SelectMany(Function(i) GetCars() _

.Select(Function(c) New With _

{.BatchNumber = i, c.VIN, c.Make, c.Model, c.Year}))

'calcula el page count

Dim pageCount = (cars.Count() / pageSize)

If (pageCount * pageSize < cars.Count()) Then pageCount += 1

For i = 0 To pageCount

txtLog.WriteLine("-----Pagina {0}------", i)

'con LINQ: Dim currentPage = cars.Skip(i * pageSize).Take(pageSize)

Dim currentPage = From c In cars

Skip (i * pageSize)

Take pageSize

Select c

For Each myCar In currentPage

txtLog.WriteLine("#{0} Car VIN:{1}, Make:{2}, Model:{3} Year:{4}", _

myCar.BatchNumber, myCar.VIN, myCar.Make, myCar.Model, myCar.Year)

Next

Next

--------------------------------------CS------------------------------------------

int pageSize = 10;

//se crea 5 copias del arreglo cars - total 25 filas

var cars = Enumerable.Range(1,5)

.SelectMany(i=>GetCars()

.Select(c=>(new {BatchNumber=i, c.VIN, c.Make, c.Model, c.Year})));

//calcula el page count

int pageCount = (cars.Count() / pageSize);

if (pageCount * pageSize < cars.Count()) pageCount++;

for(int i=0; i < pageCount; i++)

{

txtLog.WriteLine("-----Pagina {0}------", i);

var currentPage = cars.Skip(i * pageSize).Take(pageSize);

foreach (var myCar in currentPage)

{

txtLog.WriteLine("#{0} Car VIN:{1}, Make:{2}, Model:{3} Year:{4}",

myCar.BatchNumber, myCar.VIN, myCar.Make, myCar.Model, myCar.Year);

}

}

Resultado:

-----Pagina 0------
#1 Car VIN:ABC123, Make:Ford, Model:F-250 Year:2000
#1 Car VIN:DEF123, Make:BMW, Model:Z-3 Year:2005
#1 Car VIN:ABC456, Make:Audi, Model:TT Year:2008
#1 Car VIN:HIJ123, Make:VW, Model:Bug Year:1956
#1 Car VIN:DEF456, Make:Ford, Model:F-150 Year:1998
#2 Car VIN:ABC123, Make:Ford, Model:F-250 Year:2000
#2 Car VIN:DEF123, Make:BMW, Model:Z-3 Year:2005
#2 Car VIN:ABC456, Make:Audi, Model:TT Year:2008
#2 Car VIN:HIJ123, Make:VW, Model:Bug Year:1956
#2 Car VIN:DEF456, Make:Ford, Model:F-150 Year:1998
-----Pagina 1------

#3 Car VIN:ABC123, Make:Ford, Model:F-250 Year:2000
#3 Car VIN:DEF123, Make:BMW, Model:Z-3 Year:2005
#3 Car VIN:ABC456, Make:Audi, Model:TT Year:2008
#3 Car VIN:HIJ123, Make:VW, Model:Bug Year:1956
#3 Car VIN:DEF456, Make:Ford, Model:F-150 Year:1998
#4 Car VIN:ABC123, Make:Ford, Model:F-250 Year:2000
#4 Car VIN:DEF123, Make:BMW, Model:Z-3 Year:2005
#4 Car VIN:ABC456, Make:Audi, Model:TT Year:2008
#4 Car VIN:HIJ123, Make:VW, Model:Bug Year:1956
#4 Car VIN:DEF456, Make:Ford, Model:F-150 Year:1998


-----Pagina 2------
#5 Car VIN:ABC123, Make:Ford, Model:F-250 Year:2000
#5 Car VIN:DEF123, Make:BMW, Model:Z-3 Year:2005
#5 Car VIN:ABC456, Make:Audi, Model:TT Year:2008
#5 Car VIN:HIJ123, Make:VW, Model:Bug Year:1956
#5 Car VIN:DEF456, Make:Ford, Model:F-150 Year:1998

Joins
Cuando se trabaja con bases de datos, normalmente se quieren combinar datos de varias tablas para producir un conjunto de resultados combinados. LINQ permite unir dos fuentes genéricas IEnumerable, aunque estas fuentes no sean de una base de datos. Hay tres tipos de uniones: inner joins, outer joins, y cross joins.

Inner joins
Producen una salida sólo si hay una coincidencia entre ambas fuentes.

Ejemplo: una colección de coches se une a una serie de reparaciones, a través del VIN del vehículo:

--------------------------------------VB------------------------------------------

Public Class Repair

Public Property VIN() As String

Public Property Desc() As String

Public Property Cost As Decimal

End Class

Private Function GetRepairs() As List(Of Repair)

Return New List(Of Repair) From

{

New Repair With {.VIN = "ABC123", .Desc = "Change Oil", .Cost = 29.99},

New Repair With {.VIN = "DEF123", .Desc = "Rotate Tires", .Cost = 19.99},

New Repair With {.VIN = "HIJ123", .Desc = "Replace Brakes", .Cost = 200},

New Repair With {.VIN = "DEF456", .Desc = "Alignment", .Cost = 30},

New Repair With {.VIN = "ABC123", .Desc = "Fix Flat Tire", .Cost = 15},

New Repair With {.VIN = "DEF123", .Desc = "Fix Windshield", .Cost = 420},

New Repair With {.VIN = "ABC123", .Desc = "Replace Wipers", .Cost = 20},

New Repair With {.VIN = "HIJ123", .Desc = "Replace Tires", .Cost = 1000},

New Repair With {.VIN = "DEF456", .Desc = "Change Oil", .Cost = 30}

}

End Function

Dim cars = GetCars()

Dim repairs = GetRepairs()

Dim carsWithRepairs = From c In cars

Join r In repairs

On c.VIN Equals r.VIN

Order By c.VIN, r.Cost

Select New With

{

c.VIN,

c.Make,

r.Desc,

r.Cost

}

For Each item In carsWithRepairs

txtLog.WriteLine("Car VIN:{0}, Make:{1}, Description:{2} Cost:{3:C}",

item.VIN, item.Make, item.Desc, item.Cost)

Next

--------------------------------------CS------------------------------------------

public class Repair

{

public string VIN { get; set; }

public string Desc { get; set; }

public decimal Cost { get; set; }

}

private List<Repair> GetRepairs()

{

return new List<Repair>

{

new Repair {VIN = "ABC123", Desc = "Change Oil", Cost = 29.99m},

new Repair {VIN = "DEF123", Desc = "Rotate Tires", Cost =19.99m},

new Repair {VIN = "HIJ123", Desc = "Replace Brakes", Cost = 200},

new Repair {VIN = "DEF456", Desc = "Alignment", Cost = 30},

new Repair {VIN = "ABC123", Desc = "Fix Flat Tire", Cost = 15},

new Repair {VIN = "DEF123", Desc = "Fix Windshield", Cost =420},

new Repair {VIN = "ABC123", Desc = "Replace Wipers", Cost = 20},

new Repair {VIN = "HIJ123", Desc = "Replace Tires", Cost = 1000},

new Repair {VIN = "DEF456", Desc = "Change Oil", Cost = 30}

};

}

 

 

var cars = GetCars();

var repairs = GetRepairs();

var carsWithRepairs = from c in cars

join r in repairs

on c.VIN equals r.VIN

orderby c.VIN, r.Cost

select new

{

c.VIN,

c.Make,

r.Desc,

r.Cost

};

foreach (var item in carsWithRepairs)

{

txtLog.WriteLine("Car VIN:{0}, Make:{1}, Description:{2} Cost:{3:C}",

item.VIN, item.Make, item.Desc, item.Cost);

}

El resultado seria:

Car VIN:ABC123, Make:Ford, Description:Fix Flat Tire Cost:$15.00
Car VIN:ABC123, Make:Ford, Description:Replace Wipers Cost:$20.00
Car VIN:ABC123, Make:Ford, Description:Change Oil Cost:$29.99
Car VIN:DEF123, Make:BMW, Description:Rotate Tires Cost:$19.99
Car VIN:DEF123, Make:BMW, Description:Fix Windshield Cost:$420.00
Car VIN:DEF456, Make:Ford, Description:Alignment Cost:$30.00
Car VIN:DEF456, Make:Ford, Description:Change Oil Cost:$30.00
Car VIN:HIJ123, Make:VW, Description:Replace Brakes Cost:$200.00
Car VIN:HIJ123, Make:VW, Description:Replace Tires Cost:$1,000.00

Al mirar el resultado de esta consulta, el auto con el VIN de ABC456 no tenía reparación, por lo que no se muestra.

Outer Joins

Devuelve todas las filas de una de las tablas, siempre que tales filas cumplan con alguna de las condiciones de búsqueda de WHERE o HAVING.

La cláusula into crea un identificador que puede servir como una referencia a los resultados de un join , group, o select. Las referencias de la unión se asignan a la variable temp.

--------------------------------------VB------------------------------------------

Dim cars = GetCars()

Dim repairs = GetRepairs()

Dim carsWithRepairs = From c In cars

Group Join rep In repairs

On c.VIN Equals rep.VIN Into temp = Group

From r In temp.DefaultIfEmpty()

Order By c.VIN, If(r Is Nothing, 0, r.Cost)

Select New With

{

c.VIN,

c.Make,

.Desc = If(r Is Nothing, _

"***Sin Reparaciones***", r.Desc),

.Cost = If(r Is Nothing, _

0, r.Cost)

}

For Each item In carsWithRepairs

txtLog.WriteLine("Car VIN:{0}, Make:{1}, Descripcion:{2} Cost:{3:C}",

item.VIN, item.Make, item.Desc, item.Cost)

Next

--------------------------------------CS------------------------------------------

var cars = GetCars();

var repairs = GetRepairs();

var carsWithRepairs = from c in cars

join r in repairs

on c.VIN equals r.VIN into g

from r in g.DefaultIfEmpty()

orderby c.VIN, r==null?0:r.Cost

select new

{

c.VIN,

c.Make,

Desc = r==null?"***Sin Reparaciones***":r.Desc,

Cost = r==null?0:r.Cost

};

foreach (var item in carsWithRepairs)

{

txtLog.WriteLine("Car VIN:{0}, Make:{1}, Descripcion:{2} Cost:{3:C}",

item.VIN, item.Make, item.Desc, item.Cost);

}

El resultado seria:

Car VIN:ABC123, Make:Ford, Description:Fix Flat Tire Cost:$15.00
Car VIN:ABC123, Make:Ford, Description:Replace Wipers Cost:$20.00
Car VIN:ABC123, Make:Ford, Description:Change Oil Cost:$29.99
Car VIN:ABC456, Make:Audi, Description:***Sin Reparaciones*** Cost:$0.00
Car VIN:DEF123, Make:BMW, Description:Rotate Tires Cost:$19.99
Car VIN:DEF123, Make:BMW, Description:Fix Windshield Cost:$420.00
Car VIN:DEF456, Make:Ford, Description:Alignment Cost:$30.00
Car VIN:DEF456, Make:Ford, Description:Change Oil Cost:$30.00
Car VIN:HIJ123, Make:VW, Description:Replace Brakes Cost:$200.00
Car VIN:HIJ123, Make:VW, Description:Replace Tires Cost:$1,000.00

Cross Joins

Una combinación cruzada es un producto cartesiano entre dos fuentes.Un producto cartesiano unirá a cada registro del origen de los elementos exteriores con todos los elementos de la fuente interior. No se requieren claves de unión con este tipo de unión.

--------------------------------------VB------------------------------------------

Dim cars = GetCars()

Dim colors() = {"Red", "Yellow", "Blue", "Green"}

Dim carsWithRepairs = From car In cars

From color In colors

Order By car.VIN, color

Select New With

{

car.VIN,

car.Make,

car.Model,

.Color = color

}

For Each item In carsWithRepairs

txtLog.WriteLine("Car VIN:{0}, Make:{1}, Model:{2} Color:{3}",

item.VIN, item.Make, item.Model, item.Color)

Next

--------------------------------------CS------------------------------------------

var cars = GetCars();

var colors = new string[]{"Red","Yellow","Blue","Green" };

var carsWithRepairs = from car in cars

from color in colors

orderby car.VIN, color

select new

{

car.VIN,

car.Make,

car.Model,

Color=color

};

foreach (var item in carsWithRepairs)

{

txtLog.WriteLine("Car VIN:{0}, Make:{1}, Model:{2} Color:{3}",

item.VIN, item.Make, item.Model, item.Color);

}

La combinación cruzada produce una salida para cada combinación de entradas, lo que significa que es el numero de entradas de la primera multiplicada por el número de las entradas de la segunda tabla. Otra forma de implementar una combinación cruzada es utilizar el método de consulta de extensión SelectMany.

Agrupación y agregación
LINQ permite calcular los agregados para cada elemento mediante el uso de la cláusula group by.

--------------------------------------VB------------------------------------------

Dim repairs = From r In GetRepairs()

Group By VIN = r.VIN

Into grouped = Group, TotalCost = Sum(r.Cost)

For Each item In repairs

txtLog.WriteLine("Car VIN:{0}, TotalCost:{1:C}",

item.VIN, item.TotalCost)

Next

--------------------------------------CS------------------------------------------

var repairs = from r in GetRepairs()

group r by r.VIN into grouped

select new

{

VIN = grouped.Key,

TotalCost = grouped.Sum(c => c.Cost)

};

foreach (var item in repairs)

{

txtLog.WriteLine("Car VIN:{0}, Total Cost:{1:C}",

item.VIN, item.TotalCost);

}

El resultado seria:

Car VIN:ABC123, Total Cost:$64.99
Car VIN:DEF123, Total Cost:$439.99
Car VIN:HIJ123, Total Cost:$1,200.00
Car VIN:DEF456, Total Cost:$60.00

Esta consulta produce el costo total de las reparaciones de cada vehículo, pero como un auto no tenía reparación, no figura. Para mostrar todos lo autos, se debe usar left join para unir los autos con la suma de las reparaciones.

--------------------------------------VB------------------------------------------

Dim cars = GetCars()

Dim repairs = GetRepairs()

Dim carsWithRepairs = From c In cars

Group c By Key = New With {c.VIN, c.Make}

Into grouped = Group

Group Join r In repairs On Key.VIN Equals r.VIN

Into joined = Group

Select New With

{

.VIN = Key.VIN,

.Make = Key.Make,

.TotalCost = joined.Sum(Function(x) x.Cost)

}

For Each item In carsWithRepairs

txtLog.WriteLine("Car VIN:{0}, Make:{1}, Total:{2:C}", _

item.VIN, item.Make, item.TotalCost)

Next

--------------------------------------CS------------------------------------------

var cars = GetCars();

var repairs = GetRepairs();

var carsWithRepairs = from c in cars

join rep in repairs

on c.VIN equals rep.VIN into temp

from r in temp.DefaultIfEmpty()

group r by new { c.VIN, c.Make } into grouped

select new

{

VIN = grouped.Key.VIN,

Make = grouped.Key.Make,

TotalCost =

grouped.Sum(c => c == null ? 0 : c.Cost)

};

foreach (var item in carsWithRepairs)

{

txtLog.WriteLine("Car VIN:{0}, Make:{1}, Total:{2:C}",

item.VIN, item.Make, item.TotalCost);

}

}

El resultado es:

Car VIN:ABC123, Make:Ford, Total Cost:$64.99
Car VIN:DEF123, Make:BMW, Total Cost:$439.99
Car VIN:ABC456, Make:Audi, Total Cost:$0.00
Car VIN:HIJ123, Make:VW, Total Cost:$1,200.00
Car VIN:DEF456, Make:Ford, Total Cost:$60.00

Parallel LINQ (PLINQ)
LINQ paralelo, también conocido como PLINQ, es una aplicación paralela de LINQ a objetos. PLINQ implementa todos los métodos de extensión de LINQ y cuenta con operadores adicionales para las operaciones en paralelo.

En muchos escenarios, pero no todos, PLINQ puede proporcionar un aumento significativo en la velocidad mediante el uso de todas las CPU disponibles o núcleos de CPU.

Método de extensión AsParallel
El método de extensión AsParallel divide el trabajo a cada procesador o núcleo del procesador. El siguiente ejemplo de código se inicia con un cronómetro en el espacio de nombres System.Diagnostics para mostrar el tiempo transcurrido cuando esté terminado, y luego la clase Enumerable produce una secuencia de números enteros de 1 a 10.

La llamada al método AsParallel se añade a la fuente de datos. Esto hace que las iteraciones se extiendan a través del procesador disponible y los núcleos del procesador.Luego de una consulta LINQ recupera todos los números pares, pero en la consulta LINQ, la cláusula where llama a un método Compute, que tiene un retardo de un segundo usando la clase Thread, que se encuentra en el espacio de nombres System.Threading.

--------------------------------------VB------------------------------------------

Dim sw As New Stopwatch

sw.Start()

Dim source = Enumerable.Range(1, 10).AsParallel()

Dim evenNums = From num In source

Where Computing(num) Mod 2 = 0

Select num

For Each ev In evenNums

Debug.WriteLine(String.Format("{0} on Thread {1}", _

New Object() {ev, Thread.CurrentThread.GetHashCode}))

Next

sw.Stop()

Debug.WriteLine(String.Format("Done {0}", New Object() {sw.Elapsed}))

--------------------------------------CS------------------------------------------

Stopwatch sw = new Stopwatch();

sw.Start();

var source = Enumerable.Range(1, 10).AsParallel();

var evenNums = from num in source

Lesson 2: Using LINQ Queries CHAPTER 3 227

where Computing(num) % 2 == 0

select num;

foreach (var ev in evenNums)

{

Debug.WriteLine(string.Format("{0} on Thread {1}", ev,

Thread.CurrentThread.GetHashCode()));

}

sw.Stop();

Debug.WriteLine(string.Format("Done {0}", sw.Elapsed));

 

public int Computing(int num)

{

Debug.WriteLine(string.Format("Computing {0} on Thread {1}", num,

Thread.CurrentThread.GetHashCode()));

Thread.Sleep(1000);

return num;

}

Resultados mostrando los números pares, tiempo total, y el método computing:

Computing 2 on Thread 10
Computing 1 on Thread 6
Computing 3 on Thread 10
Computing 4 on Thread 6
Computing 5 on Thread 10
Computing 6 on Thread 6
Computing 7 on Thread 10
Computing 8 on Thread 6
Computing 9 on Thread 10
Computing 10 on Thread 6
2 on Thread 9
4 on Thread 9
6 on Thread 9
8 on Thread 9
10 on Thread 9
Done 00:00:05.0632071

Método de extensión ForAll

Cuando la consulta se repite con un foreach, cada iteración se sincroniza en el mismo hilo, para ser tratado, uno tras otro en el orden de la secuencia.

Si se desea es realizar cada iteración en paralelo, sin ningún orden específico, utilice el método ForAll . Tiene el mismo efecto que la ejecución de foreach, pero cada iteración esta en un subproceso diferente.

--------------------------------------VB------------------------------------------

sw.Start()

Dim source = Enumerable.Range(1, 10).AsParallel()

Dim evenNums = From num In source

Where Computing(num) Mod 2 = 0

Select num

evenNums.ForAll(Sub(ev) Debug.WriteLine(string.Format(

"{0} on Thread {1}", ev, Thread.CurrentThread.GetHashCode())))

sw.Stop()

Debug.WriteLine((string.Format("Done {0}", New Object() {sw.Elapsed}))

--------------------------------------CS------------------------------------------

Stopwatch sw = new Stopwatch();

sw.Start();

var source = Enumerable.Range(1, 10).AsParallel();

var evenNums = from num in source

where Computing(num) % 2 == 0

select num;

evenNums.ForAll(ev => Debug.WriteLine(string.Format(

"{0} on Thread {1}", ev,

Thread.CurrentThread.GetHashCode())));

sw.Stop();

Debug.WriteLine(string.Format("Done {0}", sw.Elapsed));

quedaría:

Computing 1 on Thread 9
Computing 2 on Thread 10
Computing 3 on Thread 9
2 on Thread 10
Computing 4 on Thread 10
Computing 5 on Thread 9
4 on Thread 10
Computing 6 on Thread 10
Computing 7 on Thread 9
6 on Thread 10
Computing 8 on Thread 10
Computing 9 on Thread 9
8 on Thread 10
Computing 10 on Thread 10
10 on Thread 10
Done 00:00:05.0556551

Método de extensión AsOrdered
A veces, se debe mantener el orden en la consulta, pero aún así desea la ejecución en paralelo.
El siguiente ejemplo muestra cómo se puede agregar esta llamada al método inmediatamente después del método AsParallel para mantener el orden.

--------------------------------------VB------------------------------------------

Dim sw As New Stopwatch

sw.Start()

Dim source = Enumerable.Range(1, 10).AsParallel().AsOrdered()

Dim evenNums = From num In source

Where Computing(num) Mod 2 = 0

Select num

evenNums.ForAll(Sub(ev) Debug.WriteLine(string.Format(

"{0} on Thread {1}", ev, _

Thread.CurrentThread.GetHashCode())))

sw.Stop()

Debug.WriteLine(string.Format("Done {0}", New Object() {sw.Elapsed}))

--------------------------------------CS------------------------------------------

Stopwatch sw = new Stopwatch();

sw.Start();

var source = Enumerable.Range(1, 10).AsParallel().AsOrdered();

var evenNums = from num in source

where Computing(num) % 2 == 0

select num;

evenNums.ForAll(ev => Debug.WriteLine(string.Format(

"{0} on Thread {1}", ev,

Thread.CurrentThread.GetHashCode())));

sw.Stop();

Debug.WriteLine(string.Format("Done {0}", sw.Elapsed))

Resultado:

Computing 2 on Thread 11
Computing 1 on Thread 10
2 on Thread 11
Computing 4 on Thread 11
Computing 3 on Thread 10
4 on Thread 11
Computing 6 on Thread 11
Computing 5 on Thread 10
6 on Thread 11
Computing 8 on Thread 11
Computing 7 on Thread 10
8 on Thread 11
Computing 9 on Thread 11
Computing 10 on Thread 10
10 on Thread 10
Done 00:00:05.2374586

Resumen de la lección
Esta lección proporciona una visión detallada de las clases ADO.NET desconectadas.
■ Puede utilizar consultas LINQ para proporcionar un método de consulta de cualquier tipo IEnumerable genérico.
■ Las consultas LINQ pueden ser más fácil de leer que el uso de métodos de extensión de consulta.
■ No todos los métodos de extensión de consulta mapean con las palabras clave de LINQ,por lo que aún podría ser necesario utilizar métodos de extensión con las consultas LINQ.
■ Las consultas LINQ permiten filtrar, proyectar, ordenar, unir, agrupar y agregar.
■ PLINQ ofrece una implementación paralela de LINQ que puede aumentar el rendimiento de las consultas LINQ.

 

No hay comentarios:

Publicar un comentario