使用 LINQ 进行数据转换 (C#)Data Transformations with LINQ (C#)

语言集成查询 (LINQ)Language-Integrated Query (LINQ) 不只是检索数据。is not only about retrieving data. 它也是用于转换数据的强大工具。It is also a powerful tool for transforming data. 通过使用 LINQLINQ 查询,可以使用源序列作为输入,并通过多种方式对其进行修改,以创建新的输出序列。By using a LINQLINQ query, you can use a source sequence as input and modify it in many ways to create a new output sequence. 通过排序和分组,你可以修改序列本身,而无需修改这些元素本身。You can modify the sequence itself without modifying the elements themselves by sorting and grouping. 但也许 LINQLINQ 查询最强大的功能是创建新类型。But perhaps the most powerful feature of LINQLINQ queries is the ability to create new types. 这可以在 select 子句中完成。This is accomplished in the select clause. 例如,可以执行下列任务:For example, you can perform the following tasks:

  • 将多个输入序列合并为具有新类型的单个输出序列。Merge multiple input sequences into a single output sequence that has a new type.

  • 创建其元素由源序列中每个元素的一个或多个属性组成的输出序列。Create output sequences whose elements consist of only one or several properties of each element in the source sequence.

  • 创建其元素由对源数据执行的操作结果组成的输出序列。Create output sequences whose elements consist of the results of operations performed on the source data.

  • 创建其他格式的输出序列。Create output sequences in a different format. 例如,可以将数据从 SQL 行或文本文件转换为 XML。For example, you can transform data from SQL rows or text files into XML.

这只是几个例子。These are just several examples. 当然,可以以各种方式在同一查询中组合这些转换。Of course, these transformations can be combined in various ways in the same query. 此外,一个查询的输出序列可以用作新查询的输入序列。Furthermore, the output sequence of one query can be used as the input sequence for a new query.

将多个输入联接到一个输出序列中Joining Multiple Inputs into One Output Sequence

可以使用 LINQLINQ 查询创建包含元素的输出序列,这些元素来自多个输入序列。You can use a LINQLINQ query to create an output sequence that contains elements from more than one input sequence. 以下示例演示如何组合两个内存中数据结构,但相同的原则可应用于组合来自 XML 或 SQL 或数据集源的数据。The following example shows how to combine two in-memory data structures, but the same principles can be applied to combine data from XML or SQL or DataSet sources. 假设以下两种类类型:Assume the following two class types:

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

以下示例演示了查询:The following example shows the query:

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 子句select 子句For more information, see join clause and select clause.

选择每个源元素的子集Selecting a Subset of each Source Element

有两种主要方法来选择源序列中每个元素的子集:There are two primary ways to select a subset of each element in the source sequence:

  1. 若要仅选择源元素的一个成员,请使用点操作。To select just one member of the source element, use the dot operation. 在以下示例中,假设 Customer 对象包含多个公共属性,包括名为 City 的字符串。In the following example, assume that a Customer object contains several public properties including a string named City. 在执行时,此查询将生成字符串的输出序列。When executed, this query will produce an output sequence of strings.

    var query = from cust in Customers  
                select cust.City;  
    
  2. 若要创建包含多个源元素属性的元素,可以使用带有命名对象或匿名类型的对象初始值设定项。To create elements that contain more than one property from the source element, you can use an object initializer with either a named object or an anonymous type. 以下示例演示如何使用匿名类型封装每个 Customer 元素的两个属性:The following example shows the use of an anonymous type to encapsulate two properties from each Customer element:

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

有关详细信息,请参阅对象和集合初始值设定项匿名类型For more information, see Object and Collection Initializers and Anonymous Types.

将内存中对象转换为 XMLTransforming in-Memory Objects into XML

LINQLINQ 查询可以轻松地在内存中数据结构、SQL 数据库、ADO.NET 数据集和 XML 流或文档之间转换数据。queries make it easy to transform data between in-memory data structures, SQL databases, ADO.NET Datasets and XML streams or documents. 以下示例将内存中数据结构中的对象转换为 XML 元素。The following example transforms objects in an in-memory data structure into XML elements.

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 scores = string.Join(",", student.Scores)
            select new XElement("student",
                       new XElement("First", student.First),
                       new XElement("Last", student.Last),
                       new XElement("Scores", scores)
                    ) // 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 输出:The code produces the following XML output:

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

有关详细信息,请参阅在 C# 中创建 XML 树 (LINQ to XML)For more information, see Creating XML Trees in C# (LINQ to XML).

对源元素执行操作Performing Operations on Source Elements

输出序列可能不包含源序列中的任何元素或元素属性。An output sequence might not contain any elements or element properties from the source sequence. 输出可能是使用源元素作为输入参数而计算得出的值序列。The output might instead be a sequence of values that is computed by using the source elements as input arguments. 以下简单查询在执行时会输出一串字符串,其值表示基于 double 类型的元素的源序列的计算结果。The following simple query, when it is executed, outputs a sequence of strings whose values represent a calculation based on the source sequence of elements of type double.

备注

如果查询将被转换为另一个域,则不支持在查询表达式中调用方法。Calling methods in query expressions is not supported if the query will be translated into some other domain. 例如,不能在 LINQ to SQLLINQ to SQL 中调用普通的 C# 方法,因为 SQL Server 没有用于它的上下文。For example, you cannot call an ordinary C# method in LINQ to SQLLINQ to SQL because SQL Server has no context for it. 但是,可以将存储过程映射到方法并调用这些方法。However, you can map stored procedures to methods and call those. 有关详细信息,请参阅存储过程For more information, see Stored Procedures.

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

        // Query.
        IEnumerable<string> query =
            from rad in radii
            select $"Area = {rad * rad * Math.PI:F2}";

        // 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.57
    Area = 28.27
*/

请参阅See also