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

语言集成查询 (LINQ) 不仅可用于检索数据, 而且还是一个功能强大的数据转换工具。 通过使用 LINQ 查询,您可以将源序列用作输入,并采用多种方式修改它以创建新输出序列。 您可以修改该序列,而不必修改元素通过排序和分组。本身。但是,LINQ 查询的最强大功能是能够创建新类型。 这一功能在 select 子句中实现。 例如,可以执行下列任务:

  • 将多个输入序列合并到具有新类型的单个输出序列中。

  • 创建其元素只包含源序列中的各个元素的一个或几个属性的输出序列。

  • 创建其元素包含对源数据执行的操作结果的输出序列。

  • 创建不同格式的输出序列。 例如,您可以将 SQL 行或文本文件的数据转换为 XML。

这只是几个示例。 当然,可以采用多种方式将这些转换组合在同一查询中。 另外,一个查询的输出序列可用作新查询的输入序列。

将多个输入联接到一个输出序列

可以使用 LINQ 查询来创建包含多个输入序列的元素的输出序列。 下面的示例演示如何组合两个内存中的数据结构,但组合来自 XML 或 SQL 或数据集源的数据时可应用相同的原则。 假定下面两种类类型:

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. 若要只选择源元素的一个成员,请使用点运算。 在下面的示例中,假定 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 类型的元素的源序列进行的计算。

备注

如果查询将转换为某个其他域,则不支持在查询表达式中调用方法。例如,不能在 LINQ to SQL 中调用一般 C# 方法,因为 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