C#中的LINQ

从自己的印象笔记里面整理出来,排版欠佳。见谅!

1、LINQ: 语言集成查询(Language Integrated Query)


实例:

var q=

from c in categories

join p in products on c equals p.Category into ps

select new{Category=c, Products=ps};

2、LINQ 类型


  • LINQ to Objects(或称LINQ to Collection),这是LINQ 的最基本功能,针对集合对象进行查询处理,包括基本的汇总与过滤都在这个功能内实现。
  • LINQ to SQL,这是LINQ 功能的SQL Server 数据库版本,通过LINQ 指令,可以直接查询SQL Server 数据库,而完全无须编写SQL 指令。
  • LINQ to XML,是针对XML 设计的LINQ 功能,它通过XDocument 与XElement 两个主要类的功能,进行LINQ 语法解析与XML 内的元素的查询操作。可用来替代现有以XPath方式解析XML文件的功能。
  • LINQ to DataSet(或称LINQ to ADO.NET),是为现有以DataSet或DataTable 对象开发应用程序提供支持LINQ操作的功能,以AsEnumerate() 为基础,将DataSet与DataTable内的数据转换成IEnumerable接口的操作集合,即可直接使用LINQ to Objects 的方式查询。

3、LINQ 的基础


LINQ本身以IEnumerable<T> 两个接口为基础,IEnumerable<T> 则负责泛型的集合,而目前.NET Framework 内的泛型集合类(System.Collection.Generic命名空间)都已实现IEnumerable<T> ,所以基本上在.NET Framework 内所有的集合对象都能使用LINQ 查询方法进行处理,但除了IEnumerable<T> 接口外,在LINQ 里面还包含了数个语言增强的功能。

LINQ 本身的基础建设实现与System.Linq 命名空间内,若没有使用using 引入这个命名空间的话,所有LINQ 功能都无法使用。

3.1、扩展方法


在System.Linq 命名空间内有一个叫做Enumerable 的静态类,这个类实现了许多的方法,而且这些方法都可以在任何实现IEnumerable<T> 的集合对象中找到,这个技术称为扩展方法(extension method),扩展方法赋予了程序设计语言可在现有类下扩展类的功能,而且不需要修改原本的类的程序代码。

要实现扩展方法十分简单,首先决定要为哪个类实现扩展方法,接着建立一个静态类(static class),名称建议使用“要扩展的类名称”+Extension 字样,例如像扩展Int 方法,就将类命名为Int32Extension;要扩展DateTime,就命名为DateTimeExtension。接下来在类内加入要扩展的方法,但要注意两件事:

(1)必须是静态方法,且名称不可以和现有的方法冲突。

(2)至少要有一个扩展类型输入参数,扩展类型输入参数格式必须是“this[要扩展的类名称][参数名称]”,若有两个以上的参数,则扩展类型输入参数必须放在第一个,且不能设置默认值。

下面是一个例子,想要扩展int 和double 两个对象,分为赋予产生货币字符串与百分比字符串的功能,按照扩展方法的规则,先定义Int32Extension 与DoubleExtension 两个类,并声明为static class

public static class Int32Extension

{

public  static string FormatForMoney(this int Value)

{

return Value.ToString("$###,###,###,##0");

}

}

public static class DoubleExtension

{

public static string FormatPercent(this doubel Value)

{

return Value.ToString("0.00%");

}

}

using System;

namespace ExtensionTest

{

class Program

{

static void Main(string[] args)

{

int money = 12456789;

double p = 0.123456;

Console.WriteLine("{0}",money.FormatForMoney());

Console.WriteLine("{0}", p.FormatPercent());

}

}

}

若要使用不同命名空间内的扩展方法,记得要加上命名空间声明(使用using),否则编译器会找不到扩展方法的类。

 3.2、匿名类型与对象初始化器


在开头的实例中的完整例子是这样的:

var studentScoreQuery=

from student in students

join csScore in csScores on student.ID equals csScore.ID

join dbScore in dbScores on student.ID equals dbScore.ID

select new

{

ID=student.ID,

Name=student.Name,

ScoreSum=csScore.Score+dbScore.Score,

ScoreAvg=(csScore.Score+dbScore,Score)/2

};

语法中有一个select new ,可以按做设置的属性自动产生类对象,并且自动赋予数值,这个语法包含了两个语言功能:对象初始化器与匿名类型。

对象初始化器(object initializer)允许在程序中通过声明的方式直接给对象属性进行数值的初始化,而不必刻意有参数的构造函数,可降低程序员维护多个构造函数的负担,

private static List<Student> GetStudents()

{

return new List<Student>(new []{

new Student(){

ID="001",Name="张三"

},

new Student(){

ID="002",Name="李四"

}

});

}

Person jeff=new Person("Jeff"){Age="20"}

List<Cat> cats=new List<Cat>

{

new Cat(){Name="1",Age=1},

new Cat(){Name="2",Age=2}

};

Dictionary<int,Student> students=new Dictionary<int,Student>()

{

{111,new Student{FirstName="1",LastName="2"}},

{112,new Student{FirstName="2",LastName="3"}}

}

声明匿名类型数组

var o=new

{

name="123",

value="456"

};

var o1=new []{

new {name="123",value="456"},

new {name="222",value="ddd"}

};

匿名类型的限制:

  1. 匿名类型一般只会用在用一个函数内,如果要让它被其他函数共享,则必须要动用到Reflection ,或是利用 .NET4.0 提供的动态类型(dynamic types)机制。
  2. 匿名类型只能有属性,不可以有方法、事件或字段等。
  3. 两个匿名类型对象的相等(Equal),必须要另个对象的属性值都相等才行。
  4. 匿名类型的初始化只能利用对象初始化器来进行,其属性在生成后会变成只读。

Var 类型的限制:

1、使用var类型时复制语句的右边不可以是null,否则编译器无法推断出其类型。

2、var 类型只能用于局部变量的声明,不能用于全局变量、类层级的变量或是函数的返回值。

3、var 类型不可用在匿名委派或是方法群组中。

 3.3、yield 指令与延迟查询


yield 指令,它可以让程序员以只传回每个元素的方式来自动生成 IEnumerable<T> 对象。如下两个例子所示:

private static IEnumerable<int> GetCollection1()

{

List<int> list=new List<int>();

for(int i=1;i<=5;i++)

{

list.Add(i);

}

return list;

}

private static IEnumerable<int> GetCollection2()

{

for(int i=1;i<=5;i++)

{

yield return i;

}

}

private static IEnumerable<int> GetCollection3(IEnumerable<int> NumberSeries)

{

foreach(var number in NumberSeries)

{

if(number>100)

yield break;

else

yield return number;

}

}

yield 指令的用途其实并不单单只是精简写法, yield 指令也是一种编译器魔法,会在程序编译的同时,使用yield 指令的程序段生成迭代运算的有限状态机(state machine)。这个状态机监控对象在“巡航”时所作的访问动作,在每一次“巡航”调用触发时才会真正进入集合获取数据,这个特性在LINQ 中相当重要,因为LINQ 不只是想支持集合对象,还要支持外部数据源。若没有这个特性,当LINQ 对外部数据源进行查询时,要一次取回所有数据,而无法只针对程序的需要来获取数据,这样不但会造成时间上的浪费(抓取所有数据所需的网络传输时间),也会造成空间上的浪费。所以LINQ 在设计时使用这个机制,让对集合对象的访问推迟到真正查询时才触发,这个机制称为延迟查询(Deferred Query)或延迟执行(Deferred Execution)。

3.4、Fluent Interface


Enumerbale 内所包含的LINQ 扩展方法,都返回IEnumerable<T>,这代表可以使用直接串接的方式来调用多个LINQ 函数,而不用为每个函数调用都编写一行程序,这个技术称为Fluent Interface:Fluent Interface 具有三项特性:

  1. 通过调用方法来定义对象内容。
  2. 对象会自我引用(self-referential),且新的对象内容会和最后一个对象内容等价。
  3. 通过返回void 内容(就是null 或不返回最后的对象内容)或非Fluent Interface 的对象结束。

可以直接对集合进行两次过滤:

var query=list.Where(c=>c<10000).Where(c=>c>1000);

或是提取需要的数据结构

var query=list.Where(c=>c<10000).Select(c=>new {id=c});

4、LINQ 语句


实例:查询单价1000的商品:

var query=from product in db.GetProducts()

where product.Price>1000

select product;

实例:查询商品目前的销售数字:

var query=from o in db.GetOrderDetails()

group by o.ProductID into g

select new{

ProductID=g.Key,

Qty=g.Sum(p=>p.ProductID)

};

实例:查询两个集合内都有数字的LINQ 语句:

List<int> list1=new List<int>(){1,2,3,4,5,6};

List<int> list2=new List<int>(){6,4,2,7,9,0};

var query=from item1 in list1

join item2 in list2 on item1 equals item2

select item2;

foreach(var q in query)

Console.Write("{0}",q);

1、LINQ 函数


1.1、查询结果过滤 :where()


Enumerable.Where() 是LINQ 中使用最多的函数,大多数都要针对集合对象进行过滤,因此Where()在LINQ 的操作上处处可见,Where()的主要任务是负责过滤集合中的数据:其原型如下:

public static IEnumerbale<TSouce> Where<TSource>(this IEnumerable<Tsource> source,Func<TSource,bool> predicate);

public static IEnumerable<TSource>where<TSource> (this IEnumerable<TSource> source,Func<TSource,int,bool> predicate);

Where() 的参数是用来过滤元素的条件,它要求条件必须传回bool,以确定此元素是否符合条件,或是由特定的元素开始算起(使用Func<TSource,int bool>,中间的传入参数代表该元素在集合中的索引值),例如要在一个数列集合中找出大于5的数字时:

List<int> list1=new List<int>(){6,4,2,7,9,0};

list1.Where(c=>c>5);

或者

list1.Where(c=>c>=1).Where(c=>c<=5);

list1.Where(c=>c>=1&&c<=5);

Where() 的判断标准是,只要判断函数返回true 就成立,反之则取消。

1.2、选取数据: Select()、SelectMany()


通常在编写LINQ 函数调用时较少用到选取数据的函数(因为函数调用会直接返回IEnumerable<T> 集合对象 ),但在编写LINQ语句时十分常用,在语句中若编写了select new指令,它会被编译器转换成LINQ 的Select(),Select() 的原型如下:

public static IEnumerable<TResult> Select<TSource,TResult>(this IEnumerable<TSource> source,Func<TSource,TResult> selector);

public static IEnumerable<TResult> Select<TSource,TResult>(this IEnumerable<TSource> source,Func<TSource,int,TResult> selector);

与Where() 类似,Select() 也可以按照元素所在的位置判断处理,而Select()所指定的处理式selector 必须传回一个对象,这个对象可以是现有的类型,也可以是匿名的类型,既可以通过Select() 来重新组装所需数据。例:

var query=db.OrderDetails.Where(o=>o.ID==12345).Select(o=>new{ ProductID=o.ProductID,Qty=o.Qty});

Select() 的另一个相似函数SelectMay() 则是处理有两个集合对象来源的数据选取,其原型如下:

public static IEnumerable<TResult> SelectMany<TSource,TResult>(this IEnumerable<TSource> source,Func<TSource,IEnumberable<TResult>> selector);

public static IEnumerable<TResult> SelectMany<TSource,TResult>(this IEnumerable<TSource> source,Func<TSource,int,IEnumberable<TResult>> selector);

public static IEnumerable<TResult> SelectMany<TSource,TCollection,TResult>(this IEnumerable<TSource> source,Func<TSource,IEnumberable<TCollection>> collectionSelector,Func<TSource,TCollection,TResult> resultSelector);

public static IEnumerable<TResult> SelectMany<TSource,TCollection,TResult>(this IEnumerable<TSource> source,Func<TSource,int,IEnumberable<TCollection>> collectionSelector,Func<TSource,TCollection,TResult> resultSelector);

SelectMany() 在LINQ 函数调用上较难理解,但如果把它想象成数据库的CROSS JOIN ,相对来说就容易懂了,例:

List<int> list1=new List<int>(){1,2,3,4,5,6};

List<int> list2=new List<int>(){6,4,2,7,9,0};

var query=list1.SelectMany(o=>list2);

foreach(var q in query)

Console.WriteLine("{0}",q);

输出结果:

6424790642790642790642790642790642790

因为“642790”输出了 6次,list1 内的元素是6个,所以可以知道SelectMany() 会按照list1 内的元素个数调用它的selector,并组装集合输出。

1.3、群组数据:GroupBy()、ToLookup()


汇总数据是查询机制的基本功能,而在汇总之前,必须要先将数据做群组化,才能进行统计,LINQ 的群组数据功能由Enumerable.GroupBy()函数提供。

GroupBy() 会按照给定的key(keySelector)以及内容(elementSelector),产生群组后的结果(IGroup 接口对象或是由resultSelector生成的结果对象),例:

List<int> sequence =new List<int>(){1,2,3,4,3,2,4,6,4,2,4};

var group=sequence.GroupBy(o=>o);

foreach(var g in group)

{

Console.WrilteLine("{0} count:{1}",g.Key,g.Count());//计算每个数出现的次数。

GroupBy 设置了使用数列本身值作为Key值,并且利用这个Key 分组产生分组的数据(IGrouping<TKey,TElement类型),再对分组的数据进行汇总。结果如下:

1 count: 1

2 count: 3

3 count: 2

4 count: 4

6 count: 1

若是想要在返回之前对分组后的元素做处理,可以传入elementSelector 而若是要在元素处理后产生结果的话,则可以传入resultSelector,这样返回的集合会是以resultSelector 返回的类型为主,而不是默认的IGroup接口。

除了GroupBy()能群组化数据外、另外一个具有群组化数据能力的是ToLookUp(),它可以生成具有群组化特性的集合对象,由ILookup<TKey,TElement>组成。

ToLookup()看起来和GroupBy()有些类似,但是它会另外生成一个新的集合对象,这个集合对象由ILookup<TKey,TElement>所组成,允许多个键值存在,且一个键值可包含许多关联的实值。例:

var nameValuesGroup=new[]

{

new{name="Allen", value=65,group="A"},

new{name="Abbey",value=120,group="B"},

new{name="Sue",Value=200,group="A"}

};

var lookupValues=namValuesGroup.ToLookup(c=>c.group);

foreach(var g in lookupValues)

{

Console.WriteLine("===Group: {0}===",g.Key);

foreach(var item in g)

{

Console.WriteLine("name:{0},value:{1}",item.name,item.value);

}

}

GroupBy()本身具有延迟执行的特性,而ToLookup()没有。

1.4、联接数据: Join() 与GroupJoin()


身为一个查询机制,将两个集合进行联接(join)也是理所当然的,尤其是在进行数据的对比和汇总时,联接机制显得更重要。在LINQ 函数中,有Enumerable.Join() 函数负责处理联接,其原型如下:

public static IEnumerable<TResult> Join<TOuter,TInner,TKey,TResult>(this IEnumerable<TOuter> outer,IEnumerable<TInner> inner,Func<TOutput,TKey> outerKeySelector,Func<TInner,TKey> innerKEySelector,Func<TOuter,TInner,TResult> resultSelector)

....

由原型可看到它将原本的集合视为TOuter,而将传入的集合视为TInner,儿还要决定由哪个属性或成员当Key,最后由resultSelector 来输出联接的结果。例:

var query=from item1 in list1

join item2 in list2 on item1 equals item2

select item2;

var query3=list1.Join(

list2,

item1=>item1,

item2=>item2,

(item1,item2)=>item2

);

Enumerable<T>.Join()使用的是INNER JOIN 的概念,当TInner.Key 和TOuter.Key相同时,才会将元素输出到resultSelector 作为参数。

目前常用的联接模式,INNER JOIN由 Enumerable<T>.Join() 实现,CROSS JOIN 由Enumerable<T>.SelectMany() 实现,还有一种JOIN 模式没有考虑,LEFT OUTER JOIN模式,要实现这个模式,必须要借助GroupJoin()方法来实现。

GroupJoin 和 Join() 十分相似,不过它却又Join() 和 GroupBy() 两者的功能,在Join() 的情况下,它会留下TInner 和TOuter 两边都有的值,但在GroupJoin() ,它会将TOuter 的值作为Key,并依此来对TInner 做群组化后输出,例:

var query4=from item1 in list1

join item2 in list2 on item1 equals item2 into g

from item in g.DefaultIfEmpty()

select new{ v=item1,c=item};

var query5=list1.GroupJoin(

list2,

item1=>item1,

item2=>item2,

(item1,item2)=>new {v=item1,c=item2.Count()});

1.5、数据排序:OrderBy() 与ThenBy()


数据排序是在数据处理中常见的功能,在LINQ 内的排序主要是以OrderBy 函数为主,而为了支持连续条件的排序,可加上ThenBy 函数,以便处理多重条件排序的需求。基于LINQ的延迟查询机制,排序也不是在一开始就进行的,而是在数据真的被访问时才会进行排序。因此OrderBy()在处理集合时,传递回来的是称为IOrderedEnumerable<T> 接口的对象。

OrderBy和ThenBy还有一个相似的方法,差别只在于做反向排序。OrderByDescending 和ThenByDescending。

观察函数的原型,会发现OrderBy传入的是IEnumerable<T> ,但ThenBy传入的是IOrderedEnumerable,所以一般在排序时先调用OrderBy,再使用ThenBy进行多重排序。假设一个集合有A和B两个属性,如果想要先为A排序再为B排序,则要使用OrderBy(A).ThenBy(B)的方式来进行排序,OrderBy和ThenBy 一次调用只能设置一个字段,在进行多重条件时,必须先调用OrderBy,再按需求调用ThenBy一次或多次。例:

var nameValues=new[]

{

new {name="Allen",value=64},

new {name="abbey",value=120},

new {name="slomng",value=330},

new {name="george",value=213}

};

//single sort

var sortedNames=nameValues.OrderBy(c=>c.name);

var sortedValues=nameValues.OrderBy(c=>c.value);

//multiply sort conditions

var sortedByNameValues=nameValues.OrderBy(c=>c.name).ThenBy(c=>c.value);

var sortedByValueNames=nameValues.OrderBy(c=>c.value).ThenBy(c=>c.name);

如果要设置多重排序条件,请务必使用OrderBy()加上ThenBy()的组合,若使用OrderBy +OrderBy 组合,会使得排序被执行两次,最终的结果会是最后一个OrderBy 所产生的的结果。

1.6、获取集合


LINQ 所处理的数据都由集合而来,因此将LINQ 执行的结果转换成集合也很容易。LINQ本身支持四种不同的集合生成方式,包含生成数组的ToArray()、生成列表的ToList、生成字典集合的ToDictionary 以及生成Lookup<TKey,TElement> 类的ToLookup。例:

var arrayOutput=nameValues.ToArray();

var listOutput=nameValues.ToList();

var dictOutput1=nameValues.ToDictionary(c=>c.name);

var dictOutput2=nameValues.ToDictionary(c=>c.name,c=>value);

1.7、划分并获取集合


Skip()、 SkipWhile()、 Take()、 TakeWhile()。在数据库查询时,为了达到最佳的性能,在数据量大时要进行分页处理(paging)。上面四个函数的功能就是在大集合内切出少量数据。

public static IEnumberable<TSource> Skip<TSource>(

this IEnumerable<TSource> source,

int count

)

public static IEnumberable<TSource> SkipWhile<TSource>(

this IEnumerable<TSource> source,

Func<TSource,bool> predicate

)

public static IEnumberable<TSource> SkipWhile<TSource>(

this IEnumerable<TSource> source,

Func<TSource,int ,bool> predicate

)

public static IEnumberable<TSource> Take<TSource>(

this IEnumerable<TSource> source,

int count

)

public static IEnumberable<TSource> TakeWhile<TSource>(

this IEnumerable<TSource> source,

Func<TSource,bool> predicate

)

public static IEnumberable<TSource> TakeWhile<TSource>(

this IEnumerable<TSource> source,

Func<TSource,int ,bool> predicate

)

Skip()用来在集合中跳跃,让LINQ 核心直接将游标跳到指定的位置,而不用通过“巡航”来移动,在大型集合中可节省不少时间,而SkipWhile 也有相同作用,但多了判断式,也就是跳过符合条件的元素,而不同的SkipWhile()可用来决定要跳过符合条件的或是判断跳过特定的索引值。

Take()用来传回集合中特定数量的元素,它会告知LINQ 核心直接返回它所指定的元素数量,很适合使用与分页的功能。TakeWhile 则是和SkipWhile 类似都是多了条件判断式,不过TakeWhile 在元素满足条件时,就返回该元素或是符合特定的索引值条件时返回该元素。

1.8、访问元素


IEnumerable<T>本身就是集合对象,所以针对集合对象所需要的元素访问也是必要的功能,LINQ里的元素访问功能是判断容器内是否含有元素等。

首先是获取首尾的元素,分别由First() 以及Last() 两个方法负责,它们还各有一个姐妹方法 FirstOrDefault() 以及 LastOrDefault() 前者若没有第一个或最后一个元素时,会传回null,而后者会传回其类型的默认值(基本上就是default(T)的结果)。

FirstOrDefault() 以及 LastOrDefault()都没有提供默认的设置方式,因此若想要使用非default(T)的默认值,要使用DefaultEmpty() 来设置。First() 和Last() 都能传入判断元素是否符合条件的参数,当条件判断存在时,First 会从集合的前面开始扫描,并返回扫描到符合条件的第一个元素,Last 则是反过来从集合的尾端开始扫描,并返回扫描到符合条件的第一个元素。例:

var firstLastItems=new []{"zero","two","three","four","five"};

string firstContainsO=firstLastItems.First(s=>s.Contains(‘o‘));

string lastContainsO=firstLastItems.Last(s=>s.Contains(‘0‘));

LINQ 内还有一个Single,他会在集合中只有一个元素时传回该元素,但若集合是空的或是有两个以上的元素时会调用例外处理,或是使用它的姐妹方法SingleOrDefault 传回null值,实用性比fisrt和last 低。

LINQ 提供了ElementAt() 这个方法,可按照索引值访问元素,他有个相似方法ElementAtOrDefault 作用和firstordefault/lastordefault 是相同的。当找不到元素时就返回默认值。例:

var firstLastItems=new []{"zero","two","three","four","five"};

string itematThree=firstLastITems.ElementAt(2);

若要判断集合内有没有特定值,LINQ 提供了Contains, 可以判断集合捏有没有传入的元素,但因为Contain 会判断对象是否相等,所以它另外提供了一个可传入IEqualityComparer<T> 的作为比较依据的重载(overload)方法,可用于自定义类对象的相等比较操作。

若要判断集合内有没有值,LINQ 提供了两个方法,一个是Count(), 另一个是Any(),除了可以简单判断集合内有没有值外,也可以传入判断条件来决定是否要列入计算。通常会习惯使用Count 来判断集合内是否存在任何元素,为什么要多做一个Any 呢。其实是考虑到LINQ 可能的查询对象会包含远程数据库,不一定只有本地的数据源。对于远程的数据源,如果使用Count ,要花费较高的成本来读取数据后进行计数在传回,但若是使用Any(),则远程只要判断符合条件的数据是否存在一笔即可,不需要完整计数,所以针对远程数据源,使用Any 来判断有无数据是较好的选择。针对本地的集合 any 和count 几乎没有差异。

若要判断集合内的元素是否全部符合特定条件时, 可以利用LINQ 的All(), 它可以按照传入的条件来扫描所有元素,只有在所有元素都符合条件时,或是集合时空时才会返回true ,否则会返回false。

若要按照元素的类型进行筛选的话,除了使用Where 对每个元素做类型信息判断外,LINQ 也提供了一个更简便的方法 OfType<T>(),它可以传回集合内符合T所指定类型的信息,这个方法很适合用在集合内包含了已实现了许多接口的类对象。然后使用OfType<T> 按照接口类型进行筛选。

OfType<T>还有一个类似方法Cast<T> ,功能与OfType <T> 相同,但Cast<T> 会试图把集合内的元素类型转换成T类型,若无法进行类型转换时会调用InvalidCastException 例外处理。若使用OfType<T>则不会引发例外处理。

1.9、聚合与汇总


聚合运算(aggregation)是集合数据处理的重要功能之一,基本的Max ,Min ,Sum , Average 以及可自己制定聚合规则的Aggregate()。

Aggregate 是可暂存每一步计算结果的方法,它允许程序员按照传入的条件对每个集合内的元素进行计算,而在每次调用时,他都会将前一次的结果暂存起来,并作为下次计算的传入参数。Aggregate 基本上做到三种工作,第一种是直接按照传入的条件来处理累计运算;第二种是可在调用时传入一个种子值(seed),这个种子值会在开始进行运算时作为基准使用,之后可按照第一次对种子值的运算方式开始做累计运算;第三种则是在传回之前做最后的处理,例:

double myBalance=100.0;

int[] withdrawItems={20,10,40,50,10,70,30};

double balance=withdrawItems.Aggregate(myBalance,(originbalance,nextWithdrawal)=>{

Console.WriteLine("originbalance:{0},nextWithdrawak:{1}",originbalance,nextdrawal);

Console.WriteLine("Withdrawal status:{0}",(nextWithdrawal<=originbalance)>"OK":"FAILED");

return ((nextWithdrawal<=originbalance)?(originbalance-nextWithdrawal):originbalance);

});

Console.WriteLine("Ending balance:{0}:",balance);

若要岁最终的存款值进行处理,即可使用第三个参数resultSelector,例:

var balanceStatus=

withdrawItems.Aggregate(myBalance,(originbalance,nextWithdrawal)=>{

return((nextWithdrawal<=originbalance)?(originbalance-nextWithdrawal):originbalance);

},

(finalbalance)=>

{

return (finalbalance>=1000)?"Normal":"Lower";

});

1、远程查询:IQueryable<T> 与IEnumerable<T>



     在前面讨论的LINQ 中所有的功能,基本上都基于IEnumerable<T> 接口类型(实际的实现是由Enumerable<T>类实现的)来提供。IEnumerable<T>的特性是它一定要有一个实际存在的集合才可以操作,若没有这个集合会无法使用,这点在本地集合上基本上没有问题,但是远程数据源时,因为要实现IEnumerable<T> 的功能,势必要把远程的所有数据都读取到本地,数据量小的时候可能问题不大,但若是有几万笔、几十万的数据时,或变成很严重的问题。为了支持远程数据源,LINQ的IEnumerable<T> 是无法处理的,而必须要额外提供一个针对远程数据源的专用接口。

为了达到这个需求,微软在LINQ 内建立了一个介于IEnumerable<T> 和数据源之间的提供者,这个提供者在接到来自IEnumerable<T> 的操作时,会将LINQ 本身的指令转换成表达式,在经由远程数据源的查询提供者(Query provider)将表达式转换成远程数据源可执行的查询指令,最后交由远程数据源执行,但前段仍然使用LINQ 本身的方法,这个接口称为IQueryable<T>。在LINQ的核心实现中,有一个Queryable类,这个类也实现了与Enumerable类使用的是IEnumerable<T>接口。LINQ 要求所有向远程数据库查询的功能都要实现IQueryable<T>接口,大多数提供者都实现IOrderQueryable<T>接口。

当用户对IQueryable<T> 进行操作时,Queryable 类会通过调用IQueryable<T> 内的Provider属性所设置的IQueryProvider 接口实现进行数据访问的工作,IQueryProvider是LINQ Provider 的基类。只要是想支持LINQ 的远程(外部)数据源,都必须实现IQueryProvider 接口,以支持将LINQ的查询转换成远程数据查询的工作。

LINQ 中两个特别方法:AsEnumerable 与AsQueryable 。 AsEnumerable 是将IQueryable<T> 转换成IEnumerable<T>,而 AsQueryable 正好相反。当使用AsEnumerable 将IQueryable 转换成IEnumerable 时,就变成对内存内的集合进行操作了,而不需要将查询传回到远程数据源,速度回加快很多。但是,如果集合内没有元素的话,不会向远程数据库查询数据、相反使用AsQueryable 将IEnumerable 转换成IQueryable 时,所有的LINQ 操作都会被转换成对远程数据源的查询,而不对目前在内存的集合进行操作,所以真正的查询会在远程数据源进行,与本地的内存集合无关。

2、Expression



     Expression 的功能实现于System.Linq.Expression 命名空间中,它记录了Expression表达式的结构,并提供了充分的信息让IQueryProvider 的开发人员访问其语法结构,以产生相对应的查询指令。可以把Expression表达式树的功能想象成一棵树,这棵树内包含了要运算的节点以及运算的方法(Expression Tree),而求解表达式时,只要使用中序遍历的方法,即可求解。

Expression 也是相同的原理,在使用IQueryable<T> 进行查询时,IQueryable<T>会将对IQueryable<T>所有的LINQ 进行调用,全部转换成Expression,这些Expression 会再经由LINQ Provider来转换成真正对数据源的查询指令,这些指令会包装成System.Linq.Expression 命名空间内的各种类。

3、LINQ 与ADO.NET : DataSet/DataTable 的使用


首先是DataTable,这里最常见的方法是 DataTable.Select(),然后用它规定的查询方式进行处理。CopyToDataTable<T>()可以将LINQ的查询结果转换成另一个DataTable。例:

DataSet ds=new DataSet();

ds.Locale=CultureInfo.InvariantCulture;

FillDataSet(ds);

DataTable orders=ds.Tables["SalesOrderHeader"];

IEnumerable<DataRow> query=

from order in orders.AsEnumerable();

where order.Field<DateTime>("OrderDate")>new DateTime(2001,8,1)

select order;

DataTable boundTable=query.CopyToDataTable<DataRow>();

bindingSource.DataSource=boundTable;

下面介绍DataRow 本身,在一开始DataRow 基本上提供松散类型的数据容器,因此在处理查询时都要先转换才能查询,LINQ 推出后,对DataRow类型进行了扩展,扩展的功能是强化它的强类型功能,以作为查询时简化类型转换之用,它们分别是Field<T>() 以及SetField<T>()两个方法。这两个方法都是重载(overload),可根据需求来调用必要的方法。例:

DataSet ds=new DataSet();

ds.Locale=CultureInfo.InvariantCulture;

FillDataSet(ds);

DataTable products=ds.Tables["Product"];

var query=

from product in products.AsEnumerable()

where product.Field<string>("color")=="Red"

select new

{

Name=product.Field<string>("Name"),

ProductNumber=product.Field<string>("ProductNumber"),

ListPrice=product.Field<Decimal>("ListPrice")

};

foreach(var product on query)

{

Console.WirteLine("Name:{0}",product.Name);

.....

}

除了以强类型访问DataRow 的字段外,微软也提供了强类型比较的DataRowComparer类,以处理当使用强类型方式查询DataRow 使所需要的类型转换处理工作。

var contacts=contacts1.AsEnumerable().Intersect(contacts2.AsEnumerable(),DataRowComparer.Default);

MVC 5 网站开发之美笔记 LINQ:推迟查询的执行

作者: 殷熙

1、推迟查询的执行



     在运行期间定义查询表达式时,查询就不会运行,查询会在迭代数据项时运行。

using System;

using System.Collections.Generic;

using System.Linq;

namespace OKITEST

{

class Program

{

static void Main(string[] args)

{

var names = new List<string>{ "Nino","Alberto","Juan","Mike","Phil"};

var namesWithJ = from n in names

where n.StartsWith("J")

orderby n

select n;

Console.WriteLine("First iteration");

foreach(var name in namesWithJ)

{

Console.WriteLine(name);

}

Console.WriteLine();

names.Add("John");

names.Add("Jim");

names.Add("Jimmy");

Console.WriteLine("Second iteration.");

foreach(var name in namesWithJ)

{

Console.WriteLine(name);

}

}

}

}

运行结果如下:

因为迭代在查询定义时不会进行,而是在执行每个foreach 语句时进行,所以可以看到如上的结果。

当然,还需要注意,每次在迭代中使用查询时,都会调用扩展方法。在大多数情况下,这是非常有效的,因为我们可以检测出元数据中的变化。但是在一些情况下,这是不可行的,调用扩展方法ToArray(),ToList()等可以改变这个操作。

在下面的例子送,ToList 遍历集合。返回一个实现了IList<string> 的集合。之后对返回的列表遍历两次。在两次迭代之间,数据源得到了新名称。

using System;

using System.Collections.Generic;

using System.Linq;

namespace OKITEST

{

class Program

{

static void Main(string[] args)

{

var names = new List<string>{ "Nino","Alberto","Juan","Mike","Phil"};

var namesWithJ = (from n in names

where n.StartsWith("J")

orderby n

select n).ToList();

Console.WriteLine("First iteration");

foreach(var name in namesWithJ)

{

Console.WriteLine(name);

}

Console.WriteLine();

names.Add("John");

names.Add("Jim");

names.Add("Jimmy");

Console.WriteLine("Second iteration.");

foreach(var name in namesWithJ)

{

Console.WriteLine(name);

}

}

}

}

结果如下:

时间: 2024-10-24 22:26:58

C#中的LINQ的相关文章

C++14尝鲜:在C++中实现LINQ

在C++中实现LINQ #include <iostream> #include <vector> template<typename TIterator> struct from_range { typedef typename std::iterator_traits<TIterator>::value_type value_type; TIterator current, upcoming, end; from_range(TIterator begi

在SharePoint中使用LINQ

在SharePoint中使用LINQ LINQ是一种有效且高效的查询数据的方法.使用SPMetal你可以为LINQ准备SharePoint列表.下面讲解如何准备你的列表,并创建使用LINQ的应用程序. 1. 打开命令行(管理员身份运行). 2. 导航到c:\Program Files\Common Files\Microsoft Shared\web server extensions\14\bin,并输入下面命令(确保用你自己的服务器名代替) spmetal.exe /web:http://<

C#在DataTable中使用LINQ

LINQ 查询适用于实现的数据源 IEnumerable<T>接口或System.Query.IQueryable接口. DataTable类默认是没有实现以上接口的. 所以要在DataTable中使用LINQ查询,需要调用一下AsEnumerable方法,返回一个EnumerableRowCollection<DataRow>集合. 这样我们就可以在DataTable中使用LINQ查询了 首先创建测试数据 1 private static DataTable GetStuTabl

C#中的Linq to Xml详解

这篇文章主要介绍了C#中的Linq to Xml详解,本文给出转换步骤以及大量实例,讲解了生成xml.查询并修改xml.监听xml事件.处理xml流等内容,需要的朋友可以参考下 操作之前,需要引入 程序集和命名空间 System.Xml.Linq;  一.生成Xml 为了能够在结构有一定的组织,笔者建议大家新建一个控制台项目,并且新建一个CreateXml类(以下部分都属于该类中). 并在其中写入以下属性: 代码如下: public static String Path { get { Stri

.NET Framework 3.5中的LINQ简介

1. LINQ 概览 1.1. 数据访问现状 1.2. LINQ 数据访问方式 1.3. LINQ 项目 2. 访问数组 2.1. 查询数组 2.2. 绑定到页面 3. 访问集合 3.1. 自定义 City 类 public class City { public string Name; public string Country; public int DistanceFromSeattle; } List<City> locations = GetLocations(); 3.2. 查询

LINQ系列:C#中与LINQ相关特性

1. 匿名类型 通过关键字var定义匿名类型,编译器将根据运算符右侧表达式的值来发出一个强类型. 使用匿名类型时要遵守的一些基本规则: ◊ 匿名类型必须有一个初始化值,而且这个值不能是空值(null),因为类型是根据初始化器推断出来的: ◊ 匿名类型可以用于简单类型,也可以用于复杂类型.用于定义简单类型时,其价值不大.复合匿名类型需要有成员声明: ◊ 匿名类型不能用于类的字段: ◊ 匿名类型可以在for循环中用作初始化器: ◊ 可以使用new关键字:数组的初始化器必须使用new关键字: ◊ 匿名

C#中的LINQ 基础

1.LINQ的数据源 必须可枚举的,即必须是数组或者集合 (继承了IEnumerable<T>接口就可以,注意是IEnumerable<T>,不是IEnumerable接口,不一样,继承后者只能使用foreach,不能使用linq) 定义:Linq 是一跨各种数据源和数据格式的数据模型:它在查询是,始终是把它作为一种对象来操作,可以使用基本相同的编码模型查询和数据的转换XML,SQL,ADO数据等:   对于自定义属性类,继承CollectionBase类,实现了索引器,但是没有实

Rafy 中的 Linq 查询支持(根据聚合子条件查询聚合父)

为了提高开发者的易用性,Rafy 领域实体框架在很早开始就已经支持使用 Linq 语法来查询实体了.但是只支持了一些简单的.常用的条件查询,支持的力度很有限.特别是遇到对聚合对象的查询时,就不能再使用 Linq,而只能通过构造底层查询树的接口来完成了.由于开发者的聚合查询的需求越来越多,所以本周我们将这部分进行了增强. 接下来,本文将说明 Rafy 框架原来支持的 Linq 语法,以及最新加入的聚合查询支持及用法.   使用 Linq 查询的代码示例 public WarehouseList G

译:在C#中使用LINQ To SQL

译文出处:http://www.codeproject.com/Tips/871938/LINQ-To-SQL-Using-Csharp 今天在这个话题中,我给大家分享一个在c#编程中非常有趣和十分有用的特性. 开始之前,我想告诉大家关于Linq的基本信息.比如:什么是linq?然后再来分享实际应用. 说明: LINQ = Language Integrated Query(集成查询语言) Linq是微软在.NET Framework 3.5中信增加的一个特性.它是用来查询数据库和对数据库查询的