Преобразования данных с помощью LINQ (C#)

LINQ используется не только для извлечения данных.Это также мощное средство для преобразования данных.С помощью запроса LINQ можно использовать исходную последовательность в качестве входных данных и изменять ее различными способами для создания новой выходной последовательности.Можно изменить последовательность сама без изменения элементов сами, сортировка и группирование. Однако при необходимости наиболее запросов LINQ предусмотрена удобная возможность создания новых типов.Это выполняется в предложении select.Например, можно выполнить следующие задачи.

  • Объединить несколько входных последовательностей в одну выходную последовательность, которая имеет новый тип.

  • Создать выходные последовательности, элементы которых состоят только из одного или нескольких свойств каждого элемента в исходной последовательности.

  • Создать выходные последовательности, элементы которых состоят из результатов операций, выполняемых над исходными данными.

  • Создать выходные последовательности в другом формате.Например, можно преобразовать данные из строк SQL или текстовых файлов в XML.

Это только несколько примеров.Разумеется, эти преобразования могут объединяться различными способами в одном запросе.Более того, выходные последовательности одного запроса могут использоваться как входные последовательности для нового запроса.

Соединение нескольких входных последовательностей в одну выходную

Запрос LINQ можно использовать для создания выходной последовательности, содержащей элементы из нескольких входных последовательностей.В следующем примере показано объединение двух находящихся в памяти структур данных, но те же принципы могут применяться для соединения данных из источников XML, SQL или DataSet.Предположим, что существуют два следующих типа классов.

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

В следующем примере показан запрос.

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
 */

Дополнительные сведения см. в разделах Предложение join (Справочник по C#) и Предложение "select" (справочник по C#).

Выбор подмножества каждого исходного элемента

Существует два основных способа выбора подмножества каждого элемента в исходной последовательности.

  1. Чтобы выбрать только один член исходного элемента, используйте операцию dot.В следующем примере предполагается, что объект Customer содержит несколько открытых свойств, включая строку с именем City.При выполнении этот запрос создаст выходную последовательность строк.

    var query = from cust in Customers
                select cust.City;
    
  2. Для создания элементов, содержащих более одного свойства исходного элемента, можно использовать инициализатор объектов либо с именованным объектом, либо с анонимным типом.В следующем примере показано использование анонимного типа для инкапсуляции двух свойств из каждого элемента Customer.

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

Дополнительные сведения см. в разделах Инициализаторы объектов и коллекций (Руководство по программированию в C#) и Анонимные типы (Руководство по программированию в C#).

Преобразование находящихся в памяти объектов в XML

Запросы LINQ упрощают преобразования данных между структурами данных в памяти, базами данных SQL, наборами данных ADO.NET и потоками или документами XML.В следующем примере объекты в находящейся в памяти структуре данных преобразуются в 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();
    }
}

Код формирует следующие выходные 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>

Дополнительные сведения см. в разделе Creating XML Trees in C#.

Выполнение операций над исходными элементами

Выходная последовательность может не содержать какие-либо элементы или свойства элементов из исходной последовательности.Результатом может быть последовательность значений, вычисляемых с использованием исходных элементов в качестве входных аргументов.При выполнении следующего простого запроса выводится последовательность строк, значения которых рассчитаны на основе исходной последовательности элементов типа double.

ПримечаниеПримечание

Вызов методов в выражениях запроса не поддерживается, если запрос будет перенесен в какой-либо другой домен.Например, невозможно вызвать обычный C# метод в LINQ to SQL, так как в SQL Server для него отсутствует контекст.Тем не менее, хранимые процедуры можно сопоставить методам и вызывать последние.Дополнительные сведения см. в разделе Stored Procedures: Mapping and Calling (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
*/

См. также

Задачи

Практическое руководство. Объединение данных с помощью LINQ с использованием соединений (Visual Basic)

Ссылки

Предложение "select" (справочник по C#)

Основные понятия

Выражения запросов LINQ (Руководство по программированию на C#)

Другие ресурсы

LINQ

LINQ to SQL

LINQ to DataSet

LINQ to XML