C#编程之C#中的LINQ
小标 2018-11-14 来源 : 阅读 2246 评论 0

摘要:本文主要向大家介绍了C#编程之C#中的LINQ,通过具体的内容向大家展示,希望对大家学习C#编程有所帮助。

本文主要向大家介绍了C#编程之C#中的LINQ,通过具体的内容向大家展示,希望对大家学习C#编程有所帮助。

从自己的印象笔记里面整理出来,排版欠佳。见谅!
 
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 两个接口为基础,IEnumerable 则负责泛型的集合,而目前.NET Framework 内的泛型集合类(System.Collection.Generic命名空间)都已实现IEnumerable ,所以基本上在.NET Framework 内所有的集合对象都能使用LINQ 查询方法进行处理,但除了IEnumerable 接口外,在LINQ 里面还包含了数个语言增强的功能。
     LINQ 本身的基础建设实现与System.Linq 命名空间内,若没有使用using 引入这个命名空间的话,所有LINQ 功能都无法使用。
     3.1、扩展方法

     在System.Linq 命名空间内有一个叫做Enumerable 的静态类,这个类实现了许多的方法,而且这些方法都可以在任何实现IEnumerable 的集合对象中找到,这个技术称为扩展方法(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 GetStudents()
{
     return new List(new []{
          new Student(){
               ID="001",Name="张三"
          },
          new Student(){
               ID="002",Name="李四"
          }
     });
}

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

List cats=new List
{
     new Cat(){Name="1",Age=1},
     new Cat(){Name="2",Age=2}
};

 

Dictionary students=new Dictionary()
{
     {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"}
};

     匿名类型的限制:

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

     Var 类型的限制:
      1、使用var类型时复制语句的右边不可以是null,否则编译器无法推断出其类型。
      2、var 类型只能用于局部变量的声明,不能用于全局变量、类层级的变量或是函数的返回值。
      3、var 类型不可用在匿名委派或是方法群组中。
     3.3、yield 指令与延迟查询

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

private static IEnumerable GetCollection1()
{
     List list=new List();
     
     for(int i=1;i<=5;i++)
     {
          list.Add(i);
     }
     return list;
}
 
private static IEnumerable GetCollection2()
{
     for(int i=1;i<=5;i++)
     {
          yield return i;
     }
}
 
 
private static IEnumerable GetCollection3(IEnumerable 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,这代表可以使用直接串接的方式来调用多个LINQ 函数,而不用为每个函数调用都编写一行程序,这个技术称为Fluent Interface:Fluent Interface 具有三项特性:

     通过调用方法来定义对象内容。
对象会自我引用(self-referential),且新的对象内容会和最后一个对象内容等价。
通过返回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 list1=new List(){1,2,3,4,5,6};
List list2=new List(){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 Where(this IEnumerable source,Func predicate);
     public static IEnumerablewhere (this IEnumerable source,Func predicate);

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

List list1=new List(){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 集合对象 ),但在编写LINQ语句时十分常用,在语句中若编写了select new指令,它会被编译器转换成LINQ 的Select(),Select() 的原型如下:

     public static IEnumerable Select(this IEnumerable source,Func selector);
     public static IEnumerable Select(this IEnumerable source,Func 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 SelectMany(this IEnumerable source,Func<TSource,IEnumberable> selector);
     public static IEnumerable SelectMany(this IEnumerable source,Func<TSource,int,IEnumberable> selector);
     public static IEnumerable SelectMany(this IEnumerable source,Func<TSource,IEnumberable> collectionSelector,Func resultSelector);
     public static IEnumerable SelectMany(this IEnumerable source,Func<TSource,int,IEnumberable> collectionSelector,Func resultSelector);

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

List list1=new List(){1,2,3,4,5,6};
List list2=new List(){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 sequence =new List(){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组成。
     ToLookup()看起来和GroupBy()有些类似,但是它会另外生成一个新的集合对象,这个集合对象由ILookup所组成,允许多个键值存在,且一个键值可包含许多关联的实值。例:

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 Join(this IEnumerable outer,IEnumerable inner,Func outerKeySelector,Func innerKEySelector,Func 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.Join()使用的是INNER JOIN 的概念,当TInner.Key 和TOuter.Key相同时,才会将元素输出到resultSelector 作为参数。
     目前常用的联接模式,INNER JOIN由 Enumerable.Join() 实现,CROSS JOIN 由Enumerable.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 接口的对象。
     OrderBy和ThenBy还有一个相似的方法,差别只在于做反向排序。OrderByDescending 和ThenByDescending。
     观察函数的原型,会发现OrderBy传入的是IEnumerable ,但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 类的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 Skip(
     this IEnumerable source,
     int count
)
public static IEnumberable SkipWhile(
     this IEnumerable source,
     Func predicate
)
public static IEnumberable SkipWhile(
     this IEnumerable source,
     Func predicate
)
public static IEnumberable Take(
     this IEnumerable source,
     int count
)
public static IEnumberable TakeWhile(
     this IEnumerable source,
     Func predicate
)
public static IEnumberable TakeWhile(
     this IEnumerable source,
     Func predicate
)

     Skip()用来在集合中跳跃,让LINQ 核心直接将游标跳到指定的位置,而不用通过“巡航”来移动,在大型集合中可节省不少时间,而SkipWhile 也有相同作用,但多了判断式,也就是跳过符合条件的元素,而不同的SkipWhile()可用来决定要跳过符合条件的或是判断跳过特定的索引值。
     Take()用来传回集合中特定数量的元素,它会告知LINQ 核心直接返回它所指定的元素数量,很适合使用与分页的功能。TakeWhile 则是和SkipWhile 类似都是多了条件判断式,不过TakeWhile 在元素满足条件时,就返回该元素或是符合特定的索引值条件时返回该元素。
1.8、访问元素

     IEnumerable本身就是集合对象,所以针对集合对象所需要的元素访问也是必要的功能,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 的作为比较依据的重载(overload)方法,可用于自定义类对象的相等比较操作。
     若要判断集合内有没有值,LINQ 提供了两个方法,一个是Count(), 另一个是Any(),除了可以简单判断集合内有没有值外,也可以传入判断条件来决定是否要列入计算。通常会习惯使用Count 来判断集合内是否存在任何元素,为什么要多做一个Any 呢。其实是考虑到LINQ 可能的查询对象会包含远程数据库,不一定只有本地的数据源。对于远程的数据源,如果使用Count ,要花费较高的成本来读取数据后进行计数在传回,但若是使用Any(),则远程只要判断符合条件的数据是否存在一笔即可,不需要完整计数,所以针对远程数据源,使用Any 来判断有无数据是较好的选择。针对本地的集合 any 和count 几乎没有差异。
     若要判断集合内的元素是否全部符合特定条件时, 可以利用LINQ 的All(), 它可以按照传入的条件来扫描所有元素,只有在所有元素都符合条件时,或是集合时空时才会返回true ,否则会返回false。
     若要按照元素的类型进行筛选的话,除了使用Where 对每个元素做类型信息判断外,LINQ 也提供了一个更简便的方法 OfType(),它可以传回集合内符合T所指定类型的信息,这个方法很适合用在集合内包含了已实现了许多接口的类对象。然后使用OfType 按照接口类型进行筛选。
     OfType还有一个类似方法Cast ,功能与OfType  相同,但Cast 会试图把集合内的元素类型转换成T类型,若无法进行类型转换时会调用InvalidCastException 例外处理。若使用OfType则不会引发例外处理。
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 与IEnumerable

     在前面讨论的LINQ 中所有的功能,基本上都基于IEnumerable 接口类型(实际的实现是由Enumerable类实现的)来提供。IEnumerable的特性是它一定要有一个实际存在的集合才可以操作,若没有这个集合会无法使用,这点在本地集合上基本上没有问题,但是远程数据源时,因为要实现IEnumerable 的功能,势必要把远程的所有数据都读取到本地,数据量小的时候可能问题不大,但若是有几万笔、几十万的数据时,或变成很严重的问题。为了支持远程数据源,LINQ的IEnumerable 是无法处理的,而必须要额外提供一个针对远程数据源的专用接口。
     为了达到这个需求,微软在LINQ 内建立了一个介于IEnumerable 和数据源之间的提供者,这个提供者在接到来自IEnumerable 的操作时,会将LINQ 本身的指令转换成表达式,在经由远程数据源的查询提供者(Query provider)将表达式转换成远程数据源可执行的查询指令,最后交由远程数据源执行,但前段仍然使用LINQ 本身的方法,这个接口称为IQueryable。在LINQ的核心实现中,有一个Queryable类,这个类也实现了与Enumerable类使用的是IEnumerable接口。LINQ 要求所有向远程数据库查询的功能都要实现IQueryable接口,大多数提供者都实现IOrderQueryable接口。
     当用户对IQueryable 进行操作时,Queryable 类会通过调用IQueryable 内的Provider属性所设置的IQueryProvider 接口实现进行数据访问的工作,IQueryProvider是LINQ Provider 的基类。只要是想支持LINQ 的远程(外部)数据源,都必须实现IQueryProvider 接口,以支持将LINQ的查询转换成远程数据查询的工作。
     LINQ 中两个特别方法:AsEnumerable 与AsQueryable 。 AsEnumerable 是将IQueryable 转换成IEnumerable,而 AsQueryable 正好相反。当使用AsEnumerable 将IQueryable 转换成IEnumerable 时,就变成对内存内的集合进行操作了,而不需要将查询传回到远程数据源,速度回加快很多。但是,如果集合内没有元素的话,不会向远程数据库查询数据、相反使用AsQueryable 将IEnumerable 转换成IQueryable 时,所有的LINQ 操作都会被转换成对远程数据源的查询,而不对目前在内存的集合进行操作,所以真正的查询会在远程数据源进行,与本地的内存集合无关。
2、Expression

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

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

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

DataSet ds=new DataSet();
ds.Locale=CultureInfo.InvariantCulture;
FillDataSet(ds);
DataTable orders=ds.Tables["SalesOrderHeader"];
 
 
IEnumerable query=
     from order in orders.AsEnumerable();
     where order.Field("OrderDate")>new DateTime(2001,8,1)
     select order;
DataTable boundTable=query.CopyToDataTable();
bindingSource.DataSource=boundTable;

     下面介绍DataRow 本身,在一开始DataRow 基本上提供松散类型的数据容器,因此在处理查询时都要先转换才能查询,LINQ 推出后,对DataRow类型进行了扩展,扩展的功能是强化它的强类型功能,以作为查询时简化类型转换之用,它们分别是Field() 以及SetField()两个方法。这两个方法都是重载(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("color")=="Red"
     select new
     {
          Name=product.Field("Name"),
          ProductNumber=product.Field("ProductNumber"),
          ListPrice=product.Field("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{ "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 的集合。之后对返回的列表遍历两次。在两次迭代之间,数据源得到了新名称。

using System;
using System.Collections.Generic;
using System.Linq;
 
namespace OKITEST
{
    class Program
    {
        static void Main(string[] args)
        {
            var names = new List{ "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);
            }
        }
    }
}

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标编程语言C#.NET频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 2 不喜欢 | 0
看完这篇文章有何感觉?已经有2人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程