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来实践“解释查询”,学习这些技术 ...
随机推荐
- ThinkPHP中的volist标签中使用eq标签出错
参考地址:http://blog.csdn.net/luquansen/article/details/18310855 源码: <volist id="v" name=&q ...
- 每个线程分配一个stack,每个进程分配一个heap;heap没有结构,因此寻址慢(转)
学习编程的时候,经常会看到stack这个词,它的中文名字叫做"栈". 理解这个概念,对于理解程序的运行至关重要.容易混淆的是,这个词其实有三种含义,适用于不同的场合,必须加以区分. ...
- 判断 iPhone 是否已插入 SIM 卡的方法
判断 iPhone 是否插入了 SIM 卡,可以参考苹果官网的 systemconfigure framework 教程,将下面的代码复制到头文件 extern NSString* const kCT ...
- SSL与TLS的区别以及介绍(转)
SSL:(Secure Socket Layer,安全套接字层),位于可靠的面向连接的网络层协议和应用层协议之间的一种协议层.SSL通过互相认证.使用数字签名确保完整性.使用加密确保私密性,以实现客户 ...
- hash在Coreseek 中配置bigint
304 $sphinxapi->SetSelect('id, domain_hash'); 304 $sphinxapi->SetConnectTimeout(3); 305 $sphin ...
- UI僵死分析
原因剖析 UI僵死无非只是因为UI线程因繁忙而无法去接受用户的响应.详细说来内在原因有以下两个: 正常的业务代码写在UI线程中执行,业务代码的任务繁重导致UI线程无法分身去接受用户的界面输入 UI控件 ...
- C#依据进程名称获取进程的句柄?
C#依据进程名称获取进程的句柄或C#怎样获取其它进程的句柄? 有时候标题名是动态变化的,所以不使用FindWindow方法! [StructLayout(LayoutKind.Sequential)] ...
- wbadmin delete backup删除服务器旧的镜像备份
- C++学习笔记33 转换操作符
有时候,我们要转换为类类型和类类型,同时,这两个类继承关系不存在,这时候我们就需要一些所谓的转换操作符运营商. 一个简单的例子.类别A转换为int种类 #include <iostream> ...
- 【程序猿助手】Emacs,最强的编辑器,之间的不
wx_fmt=png" alt="" style="max-width:100%; height:auto!important"> 内容简单 ...