LINQ- 子查询、组合策略、投影策略

1、Subqueries
 
A subquery is a query contained within another query's lambda expression . The following example uses a subquery to sort musicians by their last name:
string[] musos =
{ "David Gilmour", "Roger Waters", "Rick Wright", "Nick Mason" };
 
IEnumerable<string> query = musos.OrderBy (m => m.Split().Last());
 
下面的查询语句返回在 array 中所有的字符串长度最小的。
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
IEnumerable<string> outerQuery = names
    .Where (n => n.Length == names.OrderBy (n2 => n2.Length)
                                  .Select (n2 => n2.Length).First());
Tom, Jay
 
同样的表示方法:
IEnumerable<string> outerQuery = 
from n in names 
where n.Length == 
    (from n2 in names orderby n2.Length select n2.Length).First() 
select n;
因为变量(n)位于子查询的外部范围,所以我们不能重用n作为子查询的范围变量。
 
 
 
 
我们可以更简洁的表达前面的子查询:
IEnumerable<string> query =
from n in names
where n.Length == names.OrderBy (n2 => n2.Length).First().Length
select n;
 
用最小聚合函数,we can simplify the query further( 我们可以进一步简化)。
IEnumerable<string> query =
from n in names
where n.Length == names.Min (n2 => n2.Length)
select n;
 
Our example makes an ideal database query, since it would be processed as a unit, requiring only one round trip to the database server. This query, however, is inefficient for a local collection because the subquery is recalculated on each outer loop iteration. 
// 我们的示例实现了一个理想的数据库查询,因为它将作为一个单元处理,只需要到数据库服务器的一次往返。但是,这个查询对于本地集合来说效率很低,因为子查询是在每次外循环迭代时重新计算的。
 
通过单独运行子查询来避免这种低效率(这样它就不再是子查询)
We can avoid this inefficiency by running the subquery separately:
int shortest = names.Min (n => n.Length);
IEnumerable<string> query = from n in names
where n.Length == shortest
select n;
 
 
 
Subqueries and Deferred Execution 子查询和延时执行
 
子查询中的First或Count等元素或聚合操作符不会强制外部查询立即执行。延时查询依然对外部查询有效。
This is because subqueries are called indirectly—through a delegate in the case of a local query, or through an expression tree in the case of an interpreted query.
这是因为子查询是间接调用的——在本地查询中通过委托调用,在解释查询中通过表达式树调用。
 
 
2、Composition Strategies
 
In this section, we describe three strategies for building more complex queries:
//三种策略构建更复杂的查询
  • Progressive query construction
  • Using the into keyword
  • Wrapping queries
 
所有这些都是链接策略,并生成相同的运行时查询。
 
2.1、Progressive Query Building 逐步构建查询
 
At the start of the chapter, we demonstrated how you could build a fluent query progressively:
var filtered = names .Where (n => n.Contains ("a"));
var sorted = filtered .OrderBy (n => n);
var query = sorted .Select (n => n.ToUpper());
 
逐步构建查询有几个潜在的好处:
  • It can make queries easier to write. 这样写查询容易
  • You can add query operators conditionally.  方便你加入查询操作符
For example:     
if (includeFilter) query = query.Where (...)
 
This is more efficient than:
query = query.Where (n => !includeFilter || <expression>)
改为这样写避免了增加额外的查询操作,if includeFilter  is false.
because it avoids adding an extra query operator if includeFilter  is false.
 
In fluent syntax, we could write this query as a single expression—by projecting before we filter:
IEnumerable<string> query = names
.Select (n => n.Replace ("a", "").Replace ("e", "").Replace ("i", "")
.Replace ("o", "").Replace ("u", ""))
.Where (n => n.Length > 2)
.OrderBy (n => n);
 
//RESULT: { "Dck", "Hrry", "Mry" }
 
更方便的写法应该这样:
n => Regex.Replace (n, "[aeiou]", "")
 
 
2.2、The into Keyword  关键字 into
 
根据上下文,查询表达式以两种非常不同的方式解释into关键字。我们现在描述的意思是发送查询延续的信号。
 
The into keyword lets you “continue” a query after a projection and is a shortcut
for progressively querying. With into, we can rewrite the preceding query as:
//into关键字允许您在投影之后“继续”查询,这是一种快捷方式逐步查询。
IEnumerable<string> query =
from n in names
select n.Replace ("a", "").Replace ("e", "").Replace ("i", "")
    .Replace ("o", "").Replace ("u", "")
into noVowel
    where noVowel.Length > 2 orderby noVowel select noVowel;
 
当一个查询结束的时候,它得到的应该是一个序列(集合),into 语句可以让这个结果集继续为下一个查询作为输入序列。以上面的例子为例 into 把前面查询的结果集放到 noVowel 中,在其后的 where 等操作符会继续作用在这个序列上。
The only place you can use into is after a select or group clause. into “restarts” a query, allowing you to introduce fresh where, orderby, and select clauses.
to think of into as restarting a query from the perspective of a query expression,
 
//虽然从查询表达式的角度将其视为重新启动查询是最容易的,但是当将其转换为最终的流畅形式时,它就是一个查询。因此,into没有内在的性能。你也不会因为使用它而失去任何分数!
 
Scoping rules:
 
所有范围变量都不在into关键字后面的范围内。以下内容无法编译:
var query =
from n1 in names
select n1.ToUpper()
into n2 // Only n2 is visible from here on.
where n1.Contains ("x") // Illegal: n1 is not in scope.
select n2;
 
var query = names
.Select (n1 => n1.ToUpper())
.Where (n2 => n1.Contains ("x")); // Error: n1 no longer in scope
The original name (n1) is lost by the time the Where filter runs. Where’s input sequence contains only uppercase names, so it cannot filter based on n1. 
原始名称(n1)将在过滤器运行的地方丢失。其中输入序列只包含大写名,因此不能基于n1进行过滤。
 
2.3、Wrapping Queries
 
通过将一个查询封装到另一个查询中,逐步构建的查询可以表述为一个语句。
var tempQuery = tempQueryExpr
var finalQuery = from ... in tempQuery ...
 
can be reformulated as:
var finalQuery = from ... in (tempQueryExpr)
 
包装在语义上与渐进查询构建或使用into关键字相同(没有中间变量)。所有情况下的最终结果都是查询操作符的线性链。例如以下查询:
IEnumerable<string> query =
from n in names
select n.Replace ("a", "").Replace ("e", "").Replace ("i", "")
.Replace ("o", "").Replace ("u", "");
query = from n in query where n.Length > 2 orderby n select n;
 
Reformulated in wrapped form, it’s the following:
IEnumerable<string> query =
from n1 in
(
    from n2 in names
    select n2.Replace ("a", "").Replace ("e", "").Replace ("i", "")
    .Replace ("o", "").Replace ("u", "")
)
where n1.Length > 2 orderby n1 select n1;
 
When converted to fluent syntax, the result is the same linear chain of operators as in previous examples:
IEnumerable<string> query = names
.Select (n => n.Replace ("a", "").Replace ("e", "").Replace ("i", "")
.Replace ("o", "").Replace ("u", ""))
.Where (n => n.Length > 2)
.OrderBy (n => n);
 
 
 
3、Projection Strategies  投影策略
 
对象初始化。
 
We can write the following class to assist:
class TempProjectionItem
{
    public string Original; // Original name
    public string Vowelless; // Vowel-stripped name
}
 
and then project into it with object initializers:
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
IEnumerable<TempProjectionItem> temp =
from n in names
select new TempProjectionItem
{
    Original = n,
    Vowelless = n.Replace ("a", "").Replace ("e", "").Replace ("i", "")
    .Replace ("o", "").Replace ("u", "")
};
 
The result is of type IEnumerable<TempProjectionItem>, which we can subsequently query:
IEnumerable<string> query = from item in temp
where item.Vowelless.Length > 2
select item.Original;
 
Dick
Harry
Mary
 
 
Anonymous Types 匿名类型
 
匿名类型允许您在不编写特殊类的情况下构造中间结果。以上面的列子,可以将TempProjectionItem 去掉。
var intermediate = from n in names
select new
{
    Original = n,
    Vowelless = n.Replace ("a", "").Replace ("e", "").Replace ("i", "").Replace ("o", "").Replace ("u", "")
};
 
IEnumerable<string> query = from item in intermediate 
                                where item.Vowelless.Length > 2
                            select item.Original;
 
实际上这部分工作让编译器替你做了,生成一个临时类,其字段与投影的结构匹配。
 
The let Keyword let 关键字
 
let关键字在范围变量旁边引入了一个新变量,用这个新的变量来代替它。看一个列子:
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
IEnumerable<string> query =
    from n in names
        let vowelless = n.Replace ("a", "").Replace ("e", "").Replace ("i", "").Replace ("o", "").Replace ("u", "")
        where vowelless.Length > 2
    orderby vowelless
    select n; // Thanks to let, n is still in scope.
编译器通过将let子句投射到一个临时匿名类型来解析let子句,该类型同时包含范围变量和新表达式变量。换句话说,编译器将这个查询转换成前面的示例。
这个新的变量可以用在另一个范围中。你可以写任意数量的let语句,在where语句之前或之后
 
  • 它在现有元素的基础上投射新的元素 
  • 它允许表达式在查询中重复使用而不需要重写。
 
至此我们讨论的都是本地查询,是操作于实现了可枚举接口的序列上,这些操作符可以构成操作符链。接下来要学习的是 解释查询,它不同于本地查询,具体有啥不同见下回分解。
 
 
 
 
 
 

posted on 2019-09-20 20:08  拾掇的往昔  阅读(291)  评论(0编辑  收藏  举报

导航