Linq小记

官网地址:https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/linq/standard-query-operators-overview
查询操作的三个部分

所有 LINQ 查询操作都由以下三个不同的操作组成:

获取数据源。

创建查询。

执行查询。

下面的示例演示如何用源代码表示查询操作的三个部分。 为方便起见,此示例将一个整数数组用作数据源;但其中涉及的概念同样适用于其他数据源。

    class IntroToLINQ
   {        
      static void Main()
      {
    // The Three Parts of a LINQ Query:
    // 1. Data source.
    int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };

    // 2. Query creation.
    // numQuery is an IEnumerable<int>
    var numQuery =
        from num in numbers
        where (num % 2) == 0
        select num;

           // 3. Query execution.
            foreach (int num in numQuery)
          {
             Console.Write("{0,1} ", num);
          }
       }
    }

 在 LINQ 中,查询的执行与查询本身截然不同;换句话说,如果只是创建查询变量,则不会检索任何数据。

强制立即执行
对一系列源元素执行聚合函数的查询必须首先循环访问这些元素。 Count、Max、Average 和 First 就属于此类查询。 由于查询本身必须使用 foreach 以便返回结果,因此这些查询在执行时不使用显式 foreach 语句。 另外还要注意,这些类型的查询返回单个值,而不是 IEnumerable 集合。 下面的查询返回源数组中偶数的计数:

var evenNumQuery = 
from num in numbers
where (num % 2) == 0
select num;
int evenNumCount = evenNumQuery.Count();

要强制立即执行任何查询并缓存其结果,可调用 ToList 或 ToArray 方法。

List<int> numQuery2 =
(from num in numbers
 where (num % 2) == 0
 select num).ToList();

// or like this:
// numQuery3 is still an int[]

var numQuery3 =
(from num in numbers
 where (num % 2) == 0
 select num).ToArray();

此外,还可以通过在紧跟查询表达式之后的位置放置一个 foreach 循环来强制执行查询。 但是,通过调用 ToList 或 ToArray,也可以将所有数据缓存在单个集合对象中。

筛选
或许,最常见的查询操作是以布尔表达式的形式应用筛选器。 筛选器使查询仅返回表达式为 true 的元素。 将通过使用 where 子句生成结果。可使用熟悉的 C# 逻辑 AND 和 OR 运算符,在 where 子句中根据需要应用尽可能多的筛选器表达式。 例如,若要仅返回来自“London”的客户 AND 该客户名称为“Devon”,可编写以下代码:
where cust.City=="London" && cust.Name == "Devon" where cust.City == "London" || cust.City == "Paris"

中间件排序
对返回的数据进行排序通常很方便。 orderby 子句根据要排序类型的默认比较器,对返回序列中的元素排序。 例如,基于 Name 属性,可将下列查询扩展为对结果排序。 由于 Name 是字符串,默认比较器将按字母顺序从 A 到 Z 进行排序。要对结果进行从 Z 到 A 的逆序排序,请使用 orderby…descending 子句。

var queryLondonCustomers3 = 
from cust in customers
where cust.City == "London"
orderby cust.Name ascending
select cust;

分组
group 子句用于对根据您指定的键所获得的结果进行分组。 例如,可指定按 City 对结果进行分组,使来自 London 或 Paris 的所有客户位于单独的组内。 在这种情况下,cust.City 是键。

   // queryCustomersByCity is an IEnumerable<IGrouping<string, Customer>>
  var queryCustomersByCity =
  from cust in customers
  group cust by cust.City;

  // customerGroup is an IGrouping<string, Customer>
  foreach (var customerGroup in queryCustomersByCity)
 {
  Console.WriteLine(customerGroup.Key);
  foreach (Customer customer in customerGroup)
  {
      Console.WriteLine("    {0}", customer.Name);
  }
 }

使用 group 子句结束查询时,结果将以列表的形式列出。 列表中的每个元素都是具有 Key 成员的对象,列表中的元素根据该键被分组。 在循环访问生成组序列的查询时,必须使用嵌套 foreach 循环。 外层循环循环访问每个组,内层循环循环访问每个组的成员。
如果必须引用某个组操作的结果,可使用 into 关键字创建能被进一步查询的标识符。 下列查询仅返回包含两个以上客户的组:

// custQuery is an IEnumerable<IGrouping<string, Customer>>
var custQuery =
from cust in customers
group cust by cust.City into custGroup
where custGroup.Count() > 2
orderby custGroup.Key
select custGroup;

联接
联接操作在不同序列间创建关联,这些序列在数据源中未被显式模块化。 例如,可通过执行联接来查找所有位置相同的客户和分销商。 在 LINQ 中,join 子句始终作用于对象集合,而非直接作用于数据库表。

var innerJoinQuery =
from cust in customers
join dist in distributors on cust.City equals dist.City
select new { CustomerName = cust.Name, DistributorName = dist.Name };

在 LINQ 中,不必像在 SQL 中那样频繁使用 join,因为 LINQ 中的外键在对象模型中表示为包含项集合的属性。 例如 Customer 对象包含 Order 对象的集合。 不必执行联接,只需使用点表示法访问订单:
from order in Customer.Orders...

选择(投影)
select 子句生成查询结果并指定每个返回的元素的“形状”或类型。 例如,可以指定结果包含的是整个 Customer 对象、仅一个成员、成员的子集,还是某个基于计算或新对象创建的完全不同的结果类型。 当 select 子句生成除源元素副本以外的内容时,该操作称为投影。 使用投影转换数据是 LINQ 查询表达式的一种强大功能。

语言集成查询 (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 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 students = new List()
{
new Student { First="Svetlana",
Last="Omelchenko",
ID=111,
Street="123 Main Street",
City="Seattle",
Scores= new List { 97, 92, 81, 60 } },
new Student { First="Claire",
Last="O’Donnell",
ID=112,
Street="124 Main Street",
City="Redmond",
Scores= new List { 75, 84, 91, 39 } },
new Student { First="Sven",
Last="Mortensen",
ID=113,
Street="125 Main Street",
City="Lake City",
Scores= new List { 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();
}

}

选择每个源元素的子集
有两种主要方法来选择源序列中每个元素的子集:

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

将内存中对象转换为 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 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 输出:

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

LINQ 查询操作中的类型关系
若要有效编写查询,应了解完整的查询操作中的变量类型是如何全部彼此关联的。 如果了解这些关系,就能够更容易地理解文档中的 LINQ 示例和代码示例。 另外,还能了解在使用 var 隐式对变量进行类型化时的后台操作。
LINQ 查询操作在数据源、查询本身及查询执行中是强类型化的。 查询中变量的类型必须与数据源中元素的类型和 foreach 语句中迭代变量的类型兼容。 此强类型保证在编译时捕获类型错误,以便可以在用户遇到这些错误之前更正它们。
不转换源数据的查询

下图演示不对数据执行转换的 LINQ to Objects 查询操作。 源包含一个字符串序列,查询输出也是一个字符串序列
1484037-20190109134810163-893517865.png

1、数据源的类型参数决定范围变量的类型。

2、所选对象的类型决定查询变量的类型。 此处的 name 是一个字符串。 因此,查询变量是一个 IEnumerable。

3、在 foreach 语句中循环访问查询变量。 因为查询变量是一个字符串序列,所以迭代变量也是一个字符串

转换源数据的查询

下图演示对数据执行简单转换的 LINQ to SQL 查询操作。 查询将一个 Customer 对象序列用作输入,并只选择结果中的 Name 属性。 因为 Name 是一个字符串,所以查询生成一个字符串序列作为输出。
1484037-20190109134841578-1162546879.png

1、数据源的类型参数决定范围变量的类型。

2、select 语句返回 Name 属性,而非完整的 Customer 对象。 因为 Name 是一个字符串,所以 custNameQuery 的类型参数是 string,而非 Customer。

3、因为 custNameQuery 是一个字符串序列,所以 foreach 循环的迭代变量也必须是 string。

下图演示稍微复杂的转换。 select 语句返回只捕获原始 Customer 对象的两个成员的匿名类型。
1484037-20190109134932486-2124248367.png
1、数据源的类型参数始终为查询中范围变量的类型。

2、因为 select 语句生成匿名类型,所以必须使用 var 隐式类型化查询变量。

3、因为查询变量的类型是隐式的,所以 foreach 循环中的迭代变量也必须是隐式的。

让编译器推断类型信息
虽然需要了解查询操作中的类型关系,但是也可以选择让编译器执行全部工作。 关键字 var 可用于查询操作中的任何本地变量。 下图与前面讨论的第二个示例相似。 但是,编译器为查询操作中的各个变量提供强类型。
1484037-20190109135026086-1904245813.png

转载于:https://www.cnblogs.com/caozhengze/p/10243401.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值