LINQ之路(1):LINQ基础
本文将从什么是LINQ(What)、为什么使用LINQ(Why)以及如何使用LINQ(How)三个方面来进行说明。
1.什么是LINQ
LINQ(Language Integrated Query)是 Visual Studio 2008 中引入的一组功能,可为 C# 和 Visual Basic 语言语法提供强大的查询功能。 LINQ 引入了标准易学的数据查询和更新模式,可以扩展该方法来支持任何类型的数据存储。 Visual Studio 包括 LINQ 提供程序集,后者支持将 LINQ 与 .NET Framework 集合、SQL Server 数据库、ADO.NET 数据集和 XML 文档结合使用。说明来自MSDN。
2.为什么使用LINQ
我们知道,一个程序一般可能都会有多种数据源,例如:关系型数据库、XML、JSON等。在LINQ出现以前,微软还没有一套成熟的Data Mapping解决方案。我们开始的做法是针对对不同的数据源写相应的Helper类,这大大增加了工作量,而LINQ正是为了解决这个问题产生的,它用一种统一的方式即可在相应的语言中对各种数据进行查询和更新。举个例子:
var query = from x in context.T_Developer
select new
{
Developer = x,
DepartmentName = x.T_Department.Name
};
这是一个LINQ to SQL简单的例子,我们以这种更OO的方式来获取数据库中的数据,而不需要去关心它是如何操作数据库的,而且,当改成其它数据源时,只需稍微改动就能正常运行。
3.如何使用LINQ
使用LINQ之前,首先要知道语法以及标准查询运算符,下面以几个LINQ to Object例子来说明:
var developers = new List<Developer>
{
new Developer {ID = 1, Name = "Jello", DepartmentID = 1},
new Developer {ID = 2, Name = "Taffy", DepartmentID = 2},
new Developer {ID = 3, Name = "Tom", DepartmentID = 1},
new Developer {ID = 4, Name = "Lily", DepartmentID = 1}
};
var query = from x in developers
select new
{
Developer = x,
NameLength = x.Name.Length
};
这是一个基本的LINQ to Object的例子,其中,Developers是内存中的集合。初次看到会觉得很亲切,这不就是SQL么!其实真不是这么回事。

上图对该查询表达式结构做了分解说明,这里涉及到几个知识点:
1.隐式类型的局部变量,如var query
2.匿名类型,如new {...}
3.类型推断,如query变量、x范围变量等,当然,都可以具体指定类型
4.扩展方法及lambda表达式,该查询表达式等价于:
var query = developers.Select(x => new
{
Developer = x,
NameLength = x.Name.Length
});
5.延迟加载,这个特性很重要,试想,当要统计一个4G的文本文件中每行相同的字符数量,如果直接将文本文件加载到内存,这是多么恐怖!而用延迟加载每行将大大减少内存消耗。
接下来,我们将会在上面查询表达式的基础上说明其它标准查询运算符。
3.1 排序和过滤
var query = from x in developers
where x.DepartmentID == 1
orderby x.Name
select new
{
Developer = x,
NameLength = x.Name.Length
};
foreach (var item in query)
{
Console.WriteLine("{0}--{1}", item.Developer.Name, item.NameLength);
}
//Output:
Jello--5
Lily--4
Tom--3
where过滤表达式:过滤表达式会被转化为对Where扩展方法的调用,即当作进入数据流的每一个元素的谓词,只有返回true的元素才能出现在结果序列中。多个where字句时会同时起作用。
orderby排序表达式:orderby后面跟一个或多个排序规则,可以是ascending(默认)/descending,一个orderby字句中有多个排序规则时会转换为对扩展方法OrderBy(Descending)和ThenBy(Descending)的调用。当有多个orderby字句时,起作用的永远是最后一个。
3.2 联接和分组联接
我们知道,在SQL中联接(join)大致可分为inner join、outer join和cross join三种,其中outer join又可分为left outer join、right outer join和full outer join三种。在LINQ中,也有相同意义的实现。先准备数据:
var departments = new List<Department>
{
new Department {ID = 1, Name = "Product Department"},
new Department {ID = 2, Name = "Project Department"}
};
Inner join:
var query = from x in developers
join d in departments
on x.DepartmentID equals d.ID
select new
{
DeveloperName = x.Name,
DepartmentName = d.Name
};
foreach (var item in query)
{
Console.WriteLine("{0}-{1}", item.DeveloperName, item.DepartmentName);
}
//Output:
Jello-Product Department
Taffy-Project Department
Tom-Product Department
Lily-Product Department
Left outer join,是通过使用into分组,然后DefaultIfEmpty()来实现的。为了使效果更明显,这里将ID = 2的Department从departments移除:
var query = from x in developers
join d in departments
on x.DepartmentID equals d.ID into dpts
from ds in dpts.DefaultIfEmpty()
select new
{
DeveloperName = x.Name,
DepartmentName = default(Department) == ds ? "No Department" : ds.Name
};
foreach (var item in query)
{
Console.WriteLine("{0}-{1}", item.DeveloperName, item.DepartmentName);
}
//Output:
Jello-Product Department
Taffy-No Department
Tom-Product Department
Lily-Product Department
Right outer join的实现方式和Left outer join一样。
Full outer join其实Left outer join和Right outer join做Union的结果。
Cross join是一种隐式的行为,就是序列间做笛卡尔乘积:
var query = from x in developers
from d in departments
select new
{
DeveloperName = x.Name,
DepartmentName = d.Name
};
foreach (var item in query)
{
Console.WriteLine("{0}-{1}", item.DeveloperName, item.DepartmentName);
}
//Output:
Jello-Product Department
Jello-Project Department
Taffy-Product Department
Taffy-Project Department
Tom-Product Department
Tom-Project Department
Lily-Product Department
Lily-Project Department
Group join,分组联接其实我们在Left outer join中已经用到了:
var query = from d in departments
join x in developers
on d.ID equals x.DepartmentID into groupedDeveloper
select new
{
Department = d,
GroupedDeveloper = groupedDeveloper,
Count = groupedDeveloper.Count()
};
foreach (var item in query)
{
Console.WriteLine("DepartmentName:{0},Count:{1}", item.Department.Name, item.Count);
foreach (var gd in item.GroupedDeveloper)
{
Console.WriteLine(" DeveloperID:{0},DeveloperName:{1}", gd.ID, gd.Name);
}
}
//Output:
DepartmentName:Product Department,Count:3
DeveloperID:1,DeveloperName:Jello
DeveloperID:3,DeveloperName:Tom
DeveloperID:4,DeveloperName:Lily
DepartmentName:Project Department,Count:1
DeveloperID:2,DeveloperName:Taffy
在左联接查询表达式中,由于LINQ的延迟加载特性,在第一次迭代from字句序列时,会先缓存join字句序列,然后做匹配,之后的from字句序列迭代将从缓存匹配,所以,当一个大序列join一个小序列时,应该将大序列作为from字句序列,而将小序列作为join字句序列,这样小序列被缓存,而大序列由于延迟,内存消耗更小。
3.3 let字句
let字句,用于进行中间计算:
var query = from x in developers
let nameLength = x.Name.Length
orderby nameLength
select new
{
Developer = x,
NameLength = nameLength
};
foreach (var item in query)
{
Console.WriteLine("{0}--{1}", item.Developer.Name, item.NameLength);
}
//Output:
Tom--3
Lily--4
Jello--5
Taffy--5
看起来很神奇,转化为等价的扩展方法就一目了然:
var list = developers.Select(x => new { Developer = x, nameLength = x.Name.Length })
.OrderBy(x => x.nameLength)
.Select(x => new { Developer = x.Developer, NameLength = x.nameLength });
3.4 Group分组
Group分组,使用 group [投影] by [分组] 字句进行分组:
var query = from x in developers
group new { x.ID, x.Name } by x.DepartmentID;
foreach (var group in query)
{
Console.WriteLine("DepartmentID:{0}", group.Key);
foreach (var item in group)
{
Console.WriteLine(" {0}--{1}", item.ID, item.Name);
}
}
//Output:
DepartmentID:1
1--Jello
3--Tom
4--Lily
DepartmentID:2
2--Taffy
当我们想要使用另外一个投影来延续分组结果的时候,就要用到group by into来进行查询延续了。
var query = from x in developers
group x by x.DepartmentID into groupedDeveloper
select new
{
DepartmentID = groupedDeveloper.Key,
Count = groupedDeveloper.Count()
};
foreach (var item in query)
{
Console.WriteLine("DepartmentID:{0},Count:{1}", item.DepartmentID, item.Count);
}
//Output:
DepartmentID:1,Count:3
DepartmentID:2,Count:1
最后举一个稍微复杂点儿的例子:开发人员按部门分组,哪组人多哪组靠前排列,然后同部门姓名短的靠前排列。
var query = from x in developers
group x by x.DepartmentID into groupDeveloper
let count = groupDeveloper.Count()
from g in groupDeveloper
let length = g.Name.Length
orderby count descending,length
select g;
foreach (var item in query)
{
Console.WriteLine("[{0}]{1}--{2}", item.DepartmentID, item.ID, item.Name);
}
//Output:
[1]3--Tom
[1]4--Lily
[1]1--Jello
[2]2--Taffy
LINQ之路(1):LINQ基础的更多相关文章
- LINQ之路系列文章导读
本系列文章将会分为3篇来进行阐述,如下: LINQ之路(1):LINQ基础 LINQ之路(2):LINQ to SQL本质 LINQ之路(3):LINQ扩展
- LINQ之路 7:子查询、创建策略和数据转换
在前面的系列中,我们已经讨论了LINQ简单查询的大部分特性,了解了LINQ的支持计术和语法形式.至此,我们应该可以创建出大部分相对简单的LINQ查询.在本篇中,除了对前面的知识做个简单的总结,还会介绍 ...
- [转]LINQ之路系列博客导航
分享一个学习Linq的好博客:Linq之路
- LINQ之路 4:LINQ方法语法
书写LINQ查询时又两种语法可供选择:方法语法(Fluent Syntax)和查询语法(Query Expression). LINQ方法语法是非常灵活和重要的,我们在这里将描述使用链接查询运算符的方 ...
- LINQ之路(3):LINQ扩展
本篇文章将从三个方面来进行LINQ扩展的阐述:扩展查询操作符.自定义查询操作符和简单模拟LINQ to SQL. 1.扩展查询操作符 在实际的使用过程中,Enumerable或Queryable中的扩 ...
- LINQ之路(2):LINQ to SQL本质
LINQ之路(2):LINQ to SQL本质 在前面一篇文章中回顾了LINQ基本语法规则,在本文将介绍LINQ to SQL的本质.LINQ to SQL是microsoft针对SQL Server ...
- 讲讲Linq to SQL映射(基础篇)
讲讲Linq to SQL映射(基础篇) 这篇主要讲Linq to SQL基于属性的映射.即映射数据库,映射表,映射列,映射关系,映射存储过程, 映射函数.然而创建这种映射有三种方法,他们分别是OR ...
- LINQ之路16:LINQ Operators之集合运算符、Zip操作符、转换方法、生成器方法
本篇将是关于LINQ Operators的最后一篇,包括:集合运算符(Set Operators).Zip操作符.转换方法(Conversion Methods).生成器方法(Generation M ...
- LINQ之路15:LINQ Operators之元素运算符、集合方法、量词方法
本篇继续LINQ Operators的介绍,包括元素运算符/Element Operators.集合方法/Aggregation.量词/Quantifiers Methods.元素运算符从一个sequ ...
- LINQ之路10:LINQ to SQL 和 Entity Framework(下)
在本篇中,我们将接着上一篇“LINQ to SQL 和 Entity Framework(上)”的内容,继续使用LINQ to SQL和Entity Framework来实践“解释查询”,学习这些技术 ...
随机推荐
- PHP实现插入排序算法
插入排序(Insertion Sort),是一种较稳定.简单直观的排序算法.插入排序的工作原理,是通过构建有序序列,对于未排序的数据,在有序序列中从后向前扫描,找到合适的位置并将其插入.插入排序,在最 ...
- 【spring源代码分析】--Bean的解析与注冊
接着上一节继续分析,DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法: protected void parseBeanDefini ...
- iOS使用CoreImage处理图像40中可用的滤镜名称
NSString* localPath = [[NSBundle mainBundle] pathForResource:@"1" ofType:@"jpg"] ...
- 【甘道夫】Apache Hadoop 2.5.0-cdh5.2.0 HDFS Quotas 配额控制
前言 HDFS为管理员提供了针对文件夹的配额控制特性,能够控制名称配额(指定文件夹下的文件&文件夹总数),或者空间配额(占用磁盘空间的上限). 本文探究了HDFS的配额控制特性,记录了各类配额 ...
- Phpcms所有系统变量列表 Phpcms V9 文件目录结构
Phpcms所有系统变量列表 用户变量: view plaincopy to clipboardprint? $_userid 用户id $_username 用户名 $_areaid ...
- hdu3501
要我们求小于n并且不与n互素的数字的和, 那么可以转化为1->(n-1)的和减去小于n且与n互素的数字的和 首先,有gcd(n,i)=1, 那么gcd(n,n-i)=1, 这是因为如果a%s=0 ...
- CentOS7+Tomcat 生产系统部署
1 准备OS账户 安全起见,本着最小权限原则,生产系统决不同意使用root账户来执行tomcat.为此,建立新账户tomcat,并设定登录password. useradd tomcat passwd ...
- arcmap坐标点生成线和面(更正版)
一:本博客的脉络 (1 )做了例如以下更正:之前在网上搜到的结果是:arcmap坐标点生成线和面 ------ 注意该功能在ArcGIS10中没有了,当时自己也没有多想就转载了,再此做一下更正或者叫做 ...
- 解决TabActivity中子页面不通过导航跳转到还有一个页面的问题
问题:当你的导航在TabActivity中 而子页面的一个button须要切换到当中的某一个导航页面 转载请注明出处:http://blog.csdn.net/x605940745 demo下载地址: ...
- 比較Swift与HDFS话Ceph本质(by quqi99)
作者:张华 发表于:2014-06-21版权声明:能够随意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 (http://blog.csdn.net/quqi99 ) 作者将又 ...