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来实践“解释查询”,学习这些技术 ...
随机推荐
- Error与Exception的区别
错误和异常的区别(Error vs Exception) 错误和异常的区别(Error vs Exception) 今天面试问了这样一个问题,"Error" 和 "Exc ...
- Erlang学习: EUnit Testing for gen_fsm
背景:gen_fsm 是Erlang的有限状态机behavior,很实用.爱立信的一位TDD大神写了一篇怎样測试gen_fsm,这个fsm是一个交易系统,负责简单的交易员登陆,插入item,删除ite ...
- 文件下载-SpringMVC中測试
直接改动文件路径就能够.其它都不须要改动,帮助类已经为大家写好,可直接使用 1.Scroller: /** * 下载文件 * @author liupeng * @param request * @p ...
- 黄聪:Microsoft Enterprise Library 5.0 系列教程(五) Data Access Application Block
原文:黄聪:Microsoft Enterprise Library 5.0 系列教程(五) Data Access Application Block 企业库数据库访问模块通过抽象工厂模式,允许用户 ...
- Preview of Spring-framework :Spring框架的预习和自我整理
Spring简介 - 预习的自我整理 1. What's Spring? Spring是一个从实际开发中抽取出来的框架,完成了大量开发中的通用步骤,留给开发者仅仅是与特定应用相关的部分,从而提高了企业 ...
- 【原创】最近写的一个比较hack的小爬虫
目标:爬取爱漫画上面自己喜欢的一个漫画 分析阶段: 0.打开爱漫画主页,迎面就是一坨js代码..直接晕了 1.经过抓包和对html源码的分析,可以发现爱漫画通过另外一个域名发送图片,而当前域名中通过j ...
- Ubuntu下用NdisWrapper安装网卡驱动
下面是一个简单全面的使用NdisWrapper的指南.这是从Beginning Ubuntu Linux, Second Edition中提炼出来的. 这份指南是第8章的一部分.该章给出了在Ubunt ...
- 解决TabActivity中子页面不通过导航跳转到还有一个页面的问题
问题:当你的导航在TabActivity中 而子页面的一个button须要切换到当中的某一个导航页面 转载请注明出处:http://blog.csdn.net/x605940745 demo下载地址: ...
- Memento pattern
21.5 再谈备忘录的封装 备忘录是一个很特殊的对象,只有原发器对它拥有控制的权力,负责人只负责管理,而其他类无法访问到备忘录,因此我们需要对备忘录进行封装. 为了实现对备忘录对象的封装,需要对备忘录 ...
- C# The process cannot access the file because it is being used by another process
C# The process cannot access the file because it is being used by another process The process cann ...