Herramientas de usuario

Herramientas del sitio


fw:othersnet:linq

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anterior Revisión previa
Próxima revisión
Revisión previa
fw:othersnet:linq [2009/03/22 09:21]
alfred
fw:othersnet:linq [2020/05/09 09:25] (actual)
Línea 1: Línea 1:
 ====== LINQ ====== ====== LINQ ======
 +===== Sintaxis =====
  
 +En ''​System.Linq''​ existen una serie de métodos de extensión que permiten hacer consultas sobre datos (sobre xml, bases de datos...). \\ \\ 
  
 +Los siguientes son ejemplos de consultas sobre elementos en memoria:
 +<code csharp>
 +IEnumerable<​Person>​ list = lPerson.Where(p => p.firstName.StartsWith("​A"​));​
 +int maxAge = lPerson.Max(p => p.age);
 +double avg = lPerson.Average(p => p.age);
 +</​code>​
 +Pero en LINQ existen lo que serían las sintaxis de expresiones para realizar este tipo de consultas. Estas son parecidas a las sentencias sql de toda la vida y empiezan siempre por **from** y acaban por **select** siguiendo una estructura como la que sigue:
 +<code csharp>
 +from elementos_a_consultar in lista select elementos_devueltos
 +</​code>​
 +Un ejemplo:
 +<code csharp>
 +IEnumerable<​Person>​ list = from p in lPerson select p;
 +</​code>​
  
-===== DLINQ =====+===== Filtro y ordenación ​=====
  
-Podemos ​agregar ​reglas de validación a nivel de entidad, es decir, no sólo para las propiedades sino para la tabla en sí, poder validar así datos que estén relacionados.+También podemos ​agregar ​clausulas where orderby:
 <code csharp> <code csharp>
-public partial class Order { +IEnumerable<​Person>​ list = from p  
-  ​public partial void OnValidate ​() { +    in lPerson  
-    ​if (this.dataOrder ​this.dataDelivery) +    where p.firstName.StartsWith("​A"​
-      throw ("La fecha de pedido ​es mayor que la de entrega!"); +    ​orderby p.age 
-  +    select p; 
 +</code
 +En el código ​de arriba el trozo //from p in lPerson// indica lanzar una consulta LINQ sobre la colección ''​lPerson'',​ y que usaremos el parámetro ''​p''​ para representar a cada elemento que consultamos. En realidad, el nombre del parámetro ''​p'' ​es irrelevante - podría haberlo llamado ''​o'',​ ''​x'',​ ''​person''​ o de cualquier otra forma. El trozo //select p// nos indica ​que queremos devolver una secuencia IEnumerable de objetos Person como resultado de la consulta. Esto esto pasa porque la colección ''​lPerson''​ contiene objetos ​de tipo ''​Person''​. Pero si quisieramos devolver los nombres sería algo así: 
 +<code csharp>​ 
 +IEnumerable<​string>​ list = from p  
 +    in lPerson  
 +    where p.firstName.StartsWith("​A") 
 +    orderby p.age 
 +    select p.firstName
 +</​code>​ 
 +También podemos consultar los elementos relacionados o ordendar descendentemente:​ 
 +<code csharp>​ 
 +IEnumerable<​string>​ categories = from c in Categories 
 + where c.Products.Count > 5 
 + orderby c.CategoryName descending 
 + select c.CategoryName;​ 
 +</​code>​ 
 +Otro ejemplo sería el de devolver un objeto de una clase que no es la de los elementos de la lista: 
 +<code csharp>​ 
 +class AlternatePerson { 
 + string nameAge { get; set; }
 } }
 +...
 +IEnumerable<​AlternatePerson>​ friends = from p in lPeople
 + where p.LastName.StartsWith("​G"​)
 + select new AlternatePerson {
 + nameAge = p.FirstName + p.Age;
 + }
 </​code>​ </​code>​
-Al hacer una llamada ​al método ​''​SubmitChanges'' ​se llamarán a otros métodos según ​la acción realizada y donde podemos lanzar nuestras excepciones para que dicha acción no llegue a realizarsePor ejemplo, para una clase ''​Order''​ estos serían de la siguiente forma:+ 
 +===== Momento de la consulta ===== 
 + 
 +Una de las principales características de la interface IEnumerable<​T>​ es que los objetos que las implementen pueden retrasar su ejecución hasta el momento en que se introduzca en un bucle (esto es posible gracias ​al ''​yield''​). LINQ usa esta ventaja, retrasando las consultas hasta la primera vez que se itere sobre el resultadoSi nunca se itera sobre una IEnumerable<​T>,​ entonces, la consulta nunca es ejecutada. Pero si lo que queremos es ejecutar inmediatamente ​la consulta (utilizando toList o toArray) haremos:
 <code csharp> <code csharp>
-public partial class NorthwindDataContext ​+List<​Category>​ categories = (from c in db.Categories 
-  ​partial void InsertOrder ​(Order instance) { + where c.CategoryName 
-    ​this.ExecuteDynamicInsert(instance);+ select c).ToList();​ 
 +</​code>​ 
 +Si luego cambiasemos los datos de la lista inicial y volviesemos a iterarla, se volvería a ejecutar la query. Por ejemplo, en la siguiente aparece Juanjo en la segunda iteración:​ 
 +<code csharp>​ 
 +var list = from l in lPerson select l;  
 +foreach (Person p in list) 
 +    Console.WriteLine(p.firstName);​ 
 +lPerson.Add(new Person() ​firstName = "​Juanjo",​ age = 32 }); 
 +foreach (Person p in list) 
 +    Console.WriteLine(p.firstName);​ 
 +</​code>​ 
 + 
 +===== Ejemplos ===== 
 + 
 +==== Sencillos ==== 
 +=== Where + and === 
 +<code csharp>​ 
 +var expensiveInStockProducts = 
 +        from p in products 
 +        where p.UnitsInStock > 0 && p.UnitPrice > 3.00M 
 +        select p; 
 +</​code>​ 
 + 
 +=== Buscar las palabras que tengan más letras que su valor === 
 +<code csharp>​ 
 +string[] digits = { "​zero",​ "​one",​ "​two",​ "​three",​ "​four",​ "​five",​ "​six",​ "​seven",​ "​eight",​ "​nine"​ }; 
 +var shortDigits = digits.Where((digit,​ index) => digit.Length < index); 
 +</​code>​ 
 +=== Cambiando el valor === 
 +<code csharp>​ 
 +int[] numbers = { 5, 4, 1}; 
 +var numsPlusOne = 
 +        from n in numbers 
 +        select n + 1; 
 +</​code>​ 
 +Devolvería 6 5 2 
 +=== Combinar dos arrays === 
 +<code csharp>​ 
 +int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; 
 +string[] strings = { "​zero",​ "​one",​ "​two",​ "​three",​ "​four",​ "​five",​ "​six",​ "​seven",​ "​eight",​ "​nine"​ }; 
 +var textNums = 
 +        from n in numbers 
 +        select strings[n];​ 
 +</​code>​ 
 +Devolvería five, four, one... 
 + 
 +==== Grouping ==== 
 + 
 +Coger una agrupación y el tipo de agrupación que es: 
 +<code csharp>​ 
 +var wordGroups = 
 +                from w in words 
 +                group w by w[0] into g 
 +                select new { FirstLetter = g.Key, Words = g }; 
 +</​code>​ 
 +=== Anidación === 
 +<code csharp>​ 
 +var customerOrderGroups = 
 +                from c in customers 
 +                select 
 +                    new {c.CompanyName,​ 
 +                         ​YearGroups = 
 +                             from o in c.Orders 
 +                             group o by o.OrderDate.Year into yg 
 +                             ​select 
 +                                 new {Year = yg.Key, 
 +                                      MonthGroups = 
 +                                          from o in yg 
 +                                          group o by o.OrderDate.Month into mg 
 +                                          select new { Month = mg.Key, Orders = mg } 
 +                                     } 
 +                        }; 
 +</​code>​ 
 + 
 +==== Tipado anónimo ==== 
 +<code csharp>​ 
 +string[] words = { "​aPPLE",​ "​BlUeBeRrY",​ "​cHeRry"​ }; 
 +var upperLowerWords = 
 +        from w in words 
 +        select new {Upper = w.ToUpper(),​ Lower = w.ToLower()};​ 
 +foreach (var ul in upperLowerWords) { 
 +        Console.WriteLine("​Uppercase:​ {0}, Lowercase: {1}", ul.Upper, ul.Lower);​ 
 +</​code>​ 
 +Otro... 
 +<code csharp>​ 
 +var productInfos = 
 +        from p in products 
 +        select new {p.ProductName,​ p.Category, Price = p.UnitPrice};​ 
 + ​foreach (var productInfo in productInfos) { 
 +        Console.WriteLine("​{0} is in the category {1} and costs {2} per unit.",​ productInfo.ProductName,​ productInfo.Category,​ productInfo.Price);​ 
 +</​code>​ 
 +Otro... 
 +<code csharp>​ 
 +int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; 
 +var numsInPlace = numbers.Select((num,​ index) => new {Num = num, InPlace = (num == index)}); 
 +</​code>​ 
 +Otro... con asignación 
 +<code csharp>​ 
 + var orders = 
 +        from c in customers,​ 
 +                o in c.Orders, 
 +                total = o.Total 
 +        where total >= 2000.0M 
 +        select new {c.CustomerID,​ o.OrderID, total}; 
 +</​code>​ 
 +==== Relación de tablas ==== 
 +<code csharp>​ 
 +var orders = 
 +        from c in customers 
 +        where c.Region == "​WA"​ 
 +        from o in c.Orders 
 +        where o.OrderDate >= cutoffDate 
 +        select new {c.CustomerID,​ o.OrderID};​ 
 +</​code>​ 
 + 
 +==== Ordenación ==== 
 +=== Orderby === 
 +<code csharp>​ 
 +var sortedWords = 
 +        from w in words 
 +        orderby w.Length 
 +        select w; 
 +</​code>​ 
 + 
 +=== Orderby Descending === 
 +<code csharp>​ 
 +var sortedWords = 
 +        from w in words 
 +        orderby w.Length descending 
 +        select w; 
 +</​code>​ 
 + 
 +=== Cambiar el orden === 
 +<code csharp>​ 
 +var reversedIDigits = ( 
 +        from d in digits 
 +        where d[1] == '​i'​ 
 +        select d) 
 +        .Reverse();​ 
 +</​code>​ 
 + 
 +==== Contar elementos ==== 
 + 
 +=== Count === 
 +<code csharp>​ 
 +int[] factors = { 2, 2, 3, 5, 5 }; 
 +int uniqueFactors = factors.Distinct().Count();​  
 +</​code>​ 
 +Devolvería 3 
 +=== Contar los impares === 
 + 
 +<code csharp>​ 
 +int oddNumbers = numbers.Count(n => n % 2 == 1);  
 +</​code>​ 
 +=== Contar los carácteres de todas las palabras === 
 +<code csharp>​ 
 +string[] words = { "​cherry",​ "​apple",​ "​blueberry"​ }; 
 +double totalChars = words.Sum(w => w.Length);​ 
 +</​code>​ 
 +==== Concatenación de resultados ==== 
 +<code csharp>​ 
 +var customerNames = 
 +        from c in customers 
 +        select c.CompanyName;​ 
 +var productNames = 
 +        from p in products 
 +        select p.ProductName;​ 
 +var allNames = customerNames.Concat(productNames);​ 
 +</​code>​ 
 +==== Selección de elementos ==== 
 +=== El primero === 
 +<code csharp>​ 
 +Product product12 = ( 
 +        from p in products 
 +        where p.ProductID == 12 
 +        select p ) 
 +        .First(); 
 +</​code>​ 
 +=== El cuarto === 
 +<code csharp>​ 
 +int fourthLowNum = ( 
 +        from n in numbers 
 +        where n < 5 
 +        select n ) 
 +        .ElementAt(3);​ 
 +</​code>​ 
 + 
 + 
 +===== Nuevas funciones ===== 
 +==== Range ==== 
 +Generación de los números de 100 a 150: 
 +<code csharp>​ 
 +from n in Sequence.Range(100,​ 50)  
 +</​code>​ 
 +==== Repeat ==== 
 +Repetición del número 7, 10 veces: 
 +<code csharp>​ 
 +var numbers = Sequence.Repeat(7,​ 10);  
 +</​code>​ 
 + 
 +==== Distinct ==== 
 +Coge sólo uno, esquivando los elementos duplicados 
 +<code csharp>​ 
 +var categoryNames = ( 
 +        from p in products 
 +        select p.Category) 
 +        .Distinct();​ 
 +</​code>​ 
 +==== Union ==== 
 +Une dos listas del mismo tipo 
 +<code csharp>​ 
 +var uniqueNumbers = numbersA.Union(numbersB);​ 
 +</​code>​ 
 + 
 +==== Intersect ==== 
 +Coge los elementos que se repiten en las dos listas 
 +<code csharp>​ 
 +var commonNumbers = numbersA.Intersect(numbersB);​ 
 +</​code>​ 
 +==== Except ==== 
 +Coge los elementos de la primera lista que no esten en la segunda 
 +<code csharp>​ 
 +IEnumerable<​int>​ aOnlyNumbers = numbersA.Except(numbersB);​ 
 +</​code>​ 
 +==== Take ==== 
 +Coge los primeros valores 
 +<code csharp>​ 
 +var first3Numbers = numbers.Take(3);​ 
 +</​code>​ 
 +<code csharp>​ 
 +var first3WAOrders = ( 
 +                from c in customers 
 +                from o in c.Orders 
 +                where c.Region == "​WA"​ 
 +                select new {c.CustomerID,​ o.OrderID, o.OrderDate} ) 
 +                .Take(3); 
 +</​code>​ 
 + 
 + 
 +==== Skip ==== 
 +Coge los últimos números saltandose los indicados 
 +<code csharp>​ 
 +var allButFirst4Numbers = numbers.Skip(4);​ 
 +</​code>​ 
 +=== Combinación de skip y take === 
 +<code csharp>​ 
 +query.Skip(30).Take(10) 
 +</​code>​ 
 + 
 + 
 + 
 +===== Otros ===== 
 + 
 + 
 +==== let ==== 
 +La clausula ''​let''​ permite crear una variable dentro del ámbito de la query y utilizarla en la condición.  
 + 
 +<code csharp>​ 
 +var lArrivals = from a in db.PIU_ARRIVALS 
 +                let t = (DateTime.Now - a.updatedLocalTime) 
 +                where (t.Seconds > seconds) 
 +                select a; 
 +</​code>​ 
 +... O ... 
 +<code csharp>​ 
 +from car in root.Elements("​car"​) 
 +let profiles = 
 +  ​from profile in car.Elements("​profile"​) 
 +  select new 
 +    ​Name = profile.Attribute("​name"​).Value, 
 +    Value = profile.Attribute("​value"​).Value
   }   }
-  partial void UpdateOrder ​(Order instance) { +let supports = 
-    ​this.ExecuteDynamicUpdate(instance);+  from support in car.Elements("​support"​) 
 +  select new 
 +    ​Name = support.Attribute("​name"​).Value, 
 +    Value = support.Attribute("​value"​).Value
   }   }
-  partial ​void DeleteOrder ​(Order instance) { +select new Car { 
-    ​this.ExecuteDynamicDelete(instance);+  Name = car.Attribute("​name"​).Value,​ 
 +  Vendor = profiles.Single(prof => prof.Name == "​Vendor"​).Value,​ 
 +  Model = profiles.Single(prof => prof.Name == "​Model"​).Value,​ 
 +  Doors = int.Parse(profiles.Single(prof => prof.Name == "​Doors"​).Value),​ 
 +  RacingSupport = supports.Single(sup => sup.Name == "​Racing"​).Value == "​yes"​ 
 +}; 
 +</​code>​ 
 + 
 + 
 + 
 + 
 +===== Ejemplos ===== 
 +=== Ejemplo 1 === 
 +SQL: 
 +<code sql> 
 +SELECT SAE_LINEASPIENPANELINFO.INUMEROORDEN,​ SAE_LINEASPIENPANELINFO.SNOMBRELINEA,​ SAE_LINEASPIENPANELINFO.SNOMBREDESTINO,​  
 +            SAE_LINEASPIENPANELINFO.IIDLINEA,​ SAE_LINEASPIENPANELINFO.IIDTRAYECTO,​ SAE_LINEASPIENPANELINFO.IIDPARADA 
 +FROM SAE_LINEASPIENPANELINFO,​ SAE_CONFBIENPROGRAMADAS 
 +WHERE SAE_LINEASPIENPANELINFO.IIDPARADA = {0} AND SAE_LINEASPIENPANELINFO.IIDPANELINFO = {1}  
 +            AND SAE_CONFBIENPROGRAMADAS.IIDCONFLINEASACTUAL = SAE_LINEASPIENPANELINFO.IIDCONFIGURACIONLINEASPI  
 +            AND SAE_CONFBIENPROGRAMADAS.IIDPANELINFO = {1} 
 +ORDER BY SAE_LINEASPIENPANELINFO.INUMEROORDEN 
 +</​code>​ 
 +LINQ: 
 +<code csharp>​ 
 +var dades = from linies in dbdc.SAE_LINEASPIENPANELINFO 
 +            from conf in dbdc.SAE_CONFBIENPROGRAMADAS 
 +            orderby linies.INUMEROORDEN 
 +            where ((linies.IIDPARADA == p.IIDPARADA) &&​ 
 +                (linies.IIDPANELINFO == codi) &&​ 
 +                (conf.IIDCONFLINEASACTUAL == linies.IIDCONFIGURACIONLINEASPI) &&  
 +                (conf.IIDPANELINFO == codi)) 
 +            select new  
 +            { 
 +                INUMEROORDEN = linies.INUMEROORDEN,​ 
 +                SNOMBRELINEA = linies.SNOMBRELINEA,​ 
 +                SNOMBREDESTINO = linies.SNOMBREDESTINO,​ 
 +                IIDLINEA = linies.IIDLINEA,​ 
 +                IIDTRAYECTO = linies.IIDTRAYECTO,​ 
 +                IIDPARADA = linies.IIDPARADA 
 +            }; 
 +</​code>​ 
 +=== Ejemplo 2 (combinación) === 
 +Esto... 
 +<​code>​ 
 +var id_paradas = (from pt in db.SAE_PARADASTRAYECTO 
 +                       where (pt.IIDLINEA == idLinia) 
 +                       ​select pt.IIDPARADA.GetValueOrDefault(-1)).Distinct();​ 
 + 
 +return (from p in db.SAE_PARADAS 
 +        from id in id_paradas 
 +        where (p.IIDPARADA == id) 
 +        select p).ToList();​ 
 +</​code>​ 
 +Es igual a... 
 +<​code>​ 
 +return (from p in db.SAE_PARADAS 
 +        from id in ((from pt in db.SAE_PARADASTRAYECTO 
 +                       where (pt.IIDLINEA == idLinia) 
 +                       ​select pt.IIDPARADA.GetValueOrDefault(-1)).Distinct()) 
 +        where (p.IIDPARADA == id) 
 +        select p).ToList();​ 
 +</​code>​ 
 +=== Ejemplo 3 (NOT IN) === 
 +<​code>​ 
 +var query = 
 +    from c in dc.Customers 
 +    where !(from o in dc.Orders 
 +            select o.CustomerID) 
 +           ​.Contains(c.CustomerID) 
 +    select c; 
 +foreach (var c in query) Console.WriteLine( c ); 
 +</​code>​ 
 +=== Ejemplo 4 (FindAll y ForEach) === 
 +<​code>​ 
 +static ​void DisplayInstalledApplications2() 
 +{ 
 +  string registryKey = 
 +    ​@"​SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";​ 
 + 
 +  using (Microsoft.Win32.RegistryKey key =  
 +    Registry.LocalMachine.OpenSubKey(registryKey)) 
 +  { 
 +    (from a in key.GetSubKeyNames() 
 +      let r = key.OpenSubKey(a) 
 +      select new 
 +      { 
 +        Application = r.GetValue("​DisplayName"​) 
 +      }).ToList() 
 +      .FindAll(c => c.Application != null) 
 +      .ForEach(c => Console.WriteLine(c.Application));
   }   }
 } }
 </​code>​ </​code>​
-Pero podemos ir aún más lejos y hacer nuestras validaciones\comprobaciones en el método ''​SubmitChanges'' ​de una clase propia que creemos:+=== Cálculo del tamaño ​de un directorio ===
 <code csharp> <code csharp>
-public class MyNorthwindDataContext : NorthwindDataContext ​+static long DirectorySize(DirectoryInfo dInfo, bool includeSubDir) ​
-  ​public override void SubmitChanges (System.Data.Linq.ConfictMode failureMode{ +  ​// Enumerate all the files 
-    ​ChangeSet changes = this.GetChangeSet(); +  long totalSize = dInfo.EnumerateFiles(
-    ​changes.AddedEntities ... +    .Sum(file => file.Length); 
-    ​base.SubmitChanges ​(failureMode);+  // If Subdirectories are to be included 
 +  if (includeSubDir) 
 +  { 
 +    ​// Enumerate all sub-directories 
 +    ​totalSize += dInfo.EnumerateDirectories(
 +      .Sum(dir => DirectorySize(dir,​ true));
   }   }
 +  return totalSize;
 } }
 </​code>​ </​code>​
 +
 +===== Notas =====
 +  * En las querys LINQ podemos ver el código SQL generado; si se debuga se puede ver el comando sql en la variable a la que se le ha asignado dicha query.
 +  * El resultado de una sentencia LINQ puede ser asignada a un ''​DataSource''​ de un ''​GridView''​. Luego tendremos que llamar al método ''​DataBind''​ para que se muestren.
fw/othersnet/linq.1237713685.txt.gz · Última modificación: 2020/05/09 09:24 (editor externo)