Transformaciones de datos con LINQ (C#)

Language-Integrated Query (LINQ) no trata simplemente de la recuperación de datos. También es una herramienta eficaz para transformarlos. Mediante una consulta LINQ, puede utilizar una secuencia de origen como entrada y modificarla de muchas maneras para crear una nueva secuencia de salida. Puede modificar la propia secuencia sin modificar los elementos con operaciones de ordenación y agrupación. Pero quizás la característica más eficaz de las consultas de LINQ es la capacidad de crear nuevos tipos. Esto se logra en la cláusula select. Por ejemplo, puede realizar las tareas siguientes:

  • Combinar varias secuencias de entrada en una sola secuencia de salida que tiene un tipo nuevo.

  • Crear secuencias de salida cuyos elementos estén formados por una o varias propiedades de cada elemento de la secuencia de origen.

  • Crear secuencias de salida cuyos elementos estén formados por los resultados de operaciones realizadas en los datos de origen.

  • Crear secuencias de salida en un formato diferente. Por ejemplo, puede transformar datos de filas SQL o archivos de texto en XML.

Éstos son sólo algunos ejemplos. De hecho, estas transformaciones se pueden combinar de varias maneras en la misma consulta. Además, la secuencia de salida de una consulta se puede utilizar como secuencia de entrada para una nueva consulta.

Combinar varias entradas en una sola secuencia de salida

Puede utilizar una consulta LINQ para crear una secuencia de salida que contenga elementos de más de una secuencia de entrada. En el ejemplo siguiente se muestra cómo combinar dos estructuras de datos en memoria, pero se pueden aplicar los mismos principios para combinar datos de orígenes XML, SQL o de conjunto de datos. Supongamos que tenemos los dos tipos de clases siguientes:

class Student
{
    public string First { get; set; }
    public string Last {get; set;}
    public int ID { get; set; }
    public string Street { get; set; }
    public string City { get; set; }
    public List<int> Scores;
}

class Teacher
{
    public string First { get; set; }
    public string Last { get; set; }
    public int ID { get; set; } 
    public string City { get; set; }
}

En el ejemplo siguiente se muestra la consulta:

class DataTransformations
{
    static void Main()
    {
        // Create the first data source.
        List<Student> students = new List<Student>()
        {
            new Student {First="Svetlana",
                Last="Omelchenko", 
                ID=111, 
                Street="123 Main Street",
                City="Seattle",
                Scores= new List<int> {97, 92, 81, 60}},
            new Student {First="Claire",
                Last="O’Donnell", 
                ID=112,
                Street="124 Main Street",
                City="Redmond",
                Scores= new List<int> {75, 84, 91, 39}},
            new Student {First="Sven",
                Last="Mortensen",
                ID=113,
                Street="125 Main Street",
                City="Lake City",
                Scores= new List<int> {88, 94, 65, 91}},
        };

        // Create the second data source.
        List<Teacher> teachers = new List<Teacher>()
        {                
            new Teacher {First="Ann", Last="Beebe", ID=945, City = "Seattle"},
            new Teacher {First="Alex", Last="Robinson", ID=956, City = "Redmond"},
            new Teacher {First="Michiyo", Last="Sato", ID=972, City = "Tacoma"}
        };

        // Create the query. 
        var peopleInSeattle = (from student in students
                    where student.City == "Seattle" 
                    select student.Last)
                    .Concat(from teacher in teachers
                            where teacher.City == "Seattle" 
                            select teacher.Last);

        Console.WriteLine("The following students and teachers live in Seattle:");
        // Execute the query. 
        foreach (var person in peopleInSeattle)
        {
            Console.WriteLine(person);
        }

        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:
    The following students and teachers live in Seattle:
    Omelchenko
    Beebe
 */

Para obtener más información, consulte join (Cláusula, Referencia de C#) y select (Cláusula, Referencia de C#).

Seleccionar un subconjunto de cada elemento de origen

Principalmente existen dos maneras de seleccionar un subconjunto de cada elemento de la secuencia de origen:

  1. Para seleccionar un solo miembro del elemento de origen, utilice la operación con punto. En el ejemplo siguiente, supongamos que un objeto Customer contiene varias propiedades públicas que incluyen una cadena denominada City. Cuando se ejecuta, esta consulta generará una secuencia de salida de cadenas.

    var query = from cust in Customers
                select cust.City;
    
  2. Para crear elementos que contienen más de una propiedad del elemento de origen, puede utilizar un inicializador de objeto con un objeto con nombre o un tipo anónimo. En el ejemplo siguiente se muestra el uso de un tipo anónimo para encapsular dos propiedades de cada elemento Customer:

    var query = from cust in Customer
                select new {Name = cust.Name, City = cust.City};
    

Para obtener más información, consulte Inicializadores de objeto y de colección (Guía de programación de C#) y Tipos anónimos (Guía de programación de C#).

Transformar objetos en memoria en XML

Las consultas LINQ hacen que sea fácil transformar los datos entre estructuras de datos en memoria, bases de datos SQL, conjuntos de datos ADO.NET y documentos o secuencias XML. En el ejemplo siguiente se transforman los objetos de una estructura de datos en memoria en elementos XML.

class XMLTransform
{
    static void Main()
    {            
        // Create the data source by using a collection initializer. 
        // The Student class was defined previously in this topic.
        List<Student> students = new List<Student>()
        {
            new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores = new List<int>{97, 92, 81, 60}},
            new Student {First="Claire", Last="O’Donnell", ID=112, Scores = new List<int>{75, 84, 91, 39}},
            new Student {First="Sven", Last="Mortensen", ID=113, Scores = new List<int>{88, 94, 65, 91}},
        };

        // Create the query. 
        var studentsToXML = new XElement("Root",
            from student in students
            let x = String.Format("{0},{1},{2},{3}", student.Scores[0],
                    student.Scores[1], student.Scores[2], student.Scores[3])
            select new XElement("student",
                       new XElement("First", student.First),
                       new XElement("Last", student.Last),
                       new XElement("Scores", x)
                    ) // end "student"
                ); // end "Root" 

        // Execute the query.
        Console.WriteLine(studentsToXML);

        // Keep the console open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}

El código genera el siguiente resultado XML:

< Root>
  <student>
    <First>Svetlana</First>
    <Last>Omelchenko</Last>
    <Scores>97,92,81,60</Scores>
  </student>
  <student>
    <First>Claire</First>
    <Last>O'Donnell</Last>
    <Scores>75,84,91,39</Scores>
  </student>
  <student>
    <First>Sven</First>
    <Last>Mortensen</Last>
    <Scores>88,94,65,91</Scores>
  </student>
</Root>

Para obtener más información, vea Crear árboles XML en C# (LINQ to XML).

Realizar operaciones con elementos de origen

Es posible que una secuencia de salida no contenga elementos o propiedades de elemento de la secuencia de origen. Por el contrario, la salida podría ser una secuencia de valores que se calcula utilizando los elementos de origen como argumentos de entrada. Cuando se ejecuta la siguiente consulta simple, genera una secuencia de cadenas cuyos valores representan un cálculo basado en la secuencia de origen de elementos de tipo double.

Nota

No se permite llamar a métodos en las expresiones de consulta si la consulta se va a convertir en otro dominio.Por ejemplo, no puede llamar a un método normal de C# en LINQ to SQL porque SQL Server no tiene contexto para el mismo.Sin embargo, puede asignar procedimientos almacenados a los métodos y después llamar a los primeros.Para obtener más información, consulte Procedimientos almacenados [LINQ to SQL].

class FormatQuery
{
    static void Main()
    {            
        // Data source. 
        double[] radii = { 1, 2, 3 };

        // Query.
        IEnumerable<string> query =
            from rad in radii
            select String.Format("Area = {0}", (rad * rad) * 3.14);

        // Query execution.  
        foreach (string s in query)
            Console.WriteLine(s);

        // Keep the console open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:
    Area = 3.14
    Area = 12.56
    Area = 28.26
*/

Vea también

Tareas

Cómo: Combinar datos con LINQ usando cláusulas Join (Visual Basic)

Referencia

select (Cláusula, Referencia de C#)

Conceptos

Expresiones de consultas LINQ (Guía de programación de C#)

Otros recursos

LINQ (Language-Integrated Query)

LINQ a SQL [LINQ to SQL]

LINQ to DataSet

LINQ to XML