您的位置:首页 > 编程语言 > C#

LINQ查询

2016-03-08 14:38 309 查看

LINQ查询

 

LINQ是一个大家族,尽管低层的是实现各不相同,但对应用程序来说调用方式是一致的,这种一致性是通过一种松散的约束来保证的,即LINQ家族的成员你都需要提供对其数据源的通用操作,这些通用操作包括筛选,连接,排序,投影,分组等.在LINQ中,这些操作叫做LINQ运算符或LINQ运算符方法.

 

本次主要给大家讲解一下最常用到的LINQ to Object.对于LINQ to Objects来说,LINQ运算符不过是一组定义在IEnumerable<T>类型上的扩展方法.前面咱们说过IEnumerable<T>接口,它是所有可遍历集合的基础接口,由此可见LINQ的使用面很广泛.

 

LINQ to Objects的这些扩展方法定义在System.Core.dll程序集中,位于System.Linq命名空间中的Enumerable类型下,不计算重载的,大约有50多个.

 

 

查询表达式

 

C#专门为LINQ提供了一种称为查询表达式的语法,该语法编译后就相当于调用了一个扩展方法,一个简单的查询表达式如下:

static ProductCollection col = Product.GetSampleCollection();
static void Main(string[] args)
{

var query = from x in col
where x.Category == "Beer"
orderby x.Code
select new { Title = x.Name.ToUpper(), Code = x.Code };
foreach (var item in query)
{
Console.WriteLine("{0},{0}", item.Code, item.Title);
}
}

不知为何,楼主的以上代码不能运行,出现的错误如下:

未经处理的异常:  System.NotImplementedException: 未实现该方法或操作。

全部的代码上一篇博客中有,如果有大神给看出错误来了,请留言谢谢!感激不尽

 

这个查询表达式中,from声明了一个范围变量x;in关键字后面是要查询的数据源col;where后面是筛选的条件,为Lambda表达式;最后select投影操作创建了一个新的匿名类型.与这个查询表达式相对应的链式方法调用是这样的:

var query = col
.Where(x => x.Category == "Beer")
.OrderBy(x => x.Code)
.Select(x => new { Title = x.Name.ToUpper(), Code = x.Code });

 

上面的语句执行了下面3步操作:

1.筛选出Category为Beer的产品.

2.按照产品的Code进行排序.

3.构造一个匿名类型,并且为其Title和Code属性赋值,最后返回该匿名类型的集合.

 

查询表达式和方法调用可以混合使用,因此上面的语句也可以写成这样:

var query = from x in col.Where(x => x.Category == "Beer")
orderby x.Code
select new { Title = x.Name.ToUpper(), Code = x.Code };

需要注意的是,并不是所有的扩展方法都用对应的查询表达式,并且大多数扩展方法都有多个重载,某些重载方法不存在查询表达式.但是像上面这样,将两种查询表达式混用在一起可以较好地解决这个问题.不过混合使用不应该太随意,应该遵循一定得原则,这样可以保持代码的一致性和可读性.一般来说,有下面2中策略:

1.只使用链式方法调用.但在个别情况下,比如两个集合的连接操作中,使用链式方法调用会使语句变得复杂,此时应混合使用查询表达式.

2.只使用查询表达式.但对于某些查询表达式不支持的操作,例如Concat(),混合使用方法调用.

 

总的来说,使用方法调用好一些,因为查询表达式能实现的功能,方法调用都能实现,而且在大多数情况下使用起来并没有太多不方便.

 

 

延迟加载

 

既然LINQ to Objects不过是一组扩展方法,而且扩展方法非常简单,那么就可以自己编写LINQ运算符.比如,我们可以实现一个WhereNew()方法,他的作用和和Where()方法相同,也是筛选符合条件的元素:

public static IEnumerable<TSource> WhereNew<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source==null)
{
throw new ArgumentNullException();
}
if (predicate==null)
{
throw new ArgumentNullException();
}
List<TSource> inner = new List<TSource>();
foreach (TSource item in source)
{
if (predicate(item))
{
inner.Add(item);
}
}
return inner;
}

上面的代码虽然没错,但是无法写出下面这样的代码:

 

var query =from x in col
WhereNew x.Category == "Beer"
select x;

1.显然,C#编译器无法识别自定义的WhereNew关键字,上面的代码无法通过编译.由此可见,LINQ运算符扩展方法和普通的扩展方法还是有一些区别的,它们获得了C#语法和编译器的支持.

2.LINQ运算符引入了延迟加载的概念,当编写一个LINQ查询时,实际上并没有立刻执行查询操作,而是当遍历集合或者在集合上调用Count(),Max()这样的聚合函数时才会执行查询操作.

 

可以通过一个例子来看一下延迟加载的效果:

class Program
{
static void Main(string[] args)
{
List<int> list = new List<int> { 1, 2, 3 };
IEnumerable<int> query = list.Where(x => x % 2 == 0);
query.ShowConsole();

list.Add(4);
query.ShowConsole();

}
}
public static class Test
{
public static void ShowConsole(this IEnumerable list)
{
List<string> strList = new List<string>();
foreach (object item in list)
{
strList.Add(item.ToString());
}
string result = string.Join(Environment.NewLine, strList.ToArray());
Console.WriteLine(result);
}
}

这里需要说明一点,扩展方法需要放在非泛型静态类中.

 

通过以上代码可以看出,LINQ产寻只是一个定义,有点类似于声明一个SQL查询字符串string sql=”select * from product where category=’Beer’”,而遍历操作相当于调用Command.ExecuteReader().在每次进行遍历时,都会重新调用一遍查询操作.如果换成调用上面的WhereNew()则两次都会输出2.

 

至于如何才能使自定义的WhereNew和Where保持一致的行为则留给有心的读者自己去研究一下,这里楼主也不是很明白,静待大神带我起飞.

 

混合使用LINQ to Objects

 

虽然没说LINQ to SQL,但是和LINQ to Object是一样有用.因为可以进行”混搭”.对于数据量不大的表来说,可以先从数据库返回全部数据,再对它使用LINQ to Objects进行查询.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  .net C#