Linq教程.docx
- 文档编号:5233629
- 上传时间:2022-12-14
- 格式:DOCX
- 页数:14
- 大小:73.22KB
Linq教程.docx
《Linq教程.docx》由会员分享,可在线阅读,更多相关《Linq教程.docx(14页珍藏版)》请在冰豆网上搜索。
Linq教程
1、LINQ表达式
LINQ语言集成查询(LanguageIntegratedQuery)
LINQ最明显的“语言集成”部分是查询表达式。
查询表达式是使用C#3.0中引入的声明性查询语法编写的。
通过使用查询语法,您甚至可以使用最少的代码对数据源执行复杂的筛选、排序和分组操作。
您使用相同的基本查询表达式模式来查询和转换SQL数据库、ADO.NET数据集、XML文档和流以及.NET集合中的数据。
下面的示例演示了完整的查询操作。
完整操作包括创建数据源、定义查询表达式,以及在foreach语句中执行查询。
classLINQQueryExpressions
{
staticvoidMain()
{
//Specifythedatasource.
int[]scores=newint[]{97,92,81,60,89,45,34,78};
//Definethequeryexpression.
IEnumerable
fromscoreinscores
wherescore>80
selectscore;
//Executethequery.
foreach(intiinscoreQuery)
{
Console.Write(i+"");
}
}
}
//Output:
97928189
2、LINQ查询
“查询”是指一组指令,这些指令描述要从一个或多个给定数据源检索的数据以及返回的数据应该使用的格式和组织形式。
查询不同于它所产生的结果。
通常,源数据会在逻辑上组织为相同种类的元素序列。
SQL数据库表包含一个行序列。
与此类似,ADO.NETDataTable包含一个DataRow对象序列。
在XML文件中,有一个XML元素“序列”(不过这些元素按分层形式组织为树结构)。
内存中的集合包含一个对象序列。
从应用程序的角度来看,原始源数据的具体类型和结构并不重要。
应用程序始终将源数据视为一个IEnumerable<(Of<(T>)>)或IQueryable<(Of<(T>)>)集合。
在LINQtoSQL中,源数据显示为一个IEnumerable
在LINQtoDataSet中,它是一个IEnumerable
在LINQtoSQL中,它是您定义用来表示SQL表中数据的任何自定义对象的IEnumerable或IQueryable。
●检索一个元素子集以产生一个新序列,但不修改单个元素。
然后,查询可以按各种方式对返回的序列进行排序或分组,如下面的示例所示(假定scores是int[]):
IEnumerable
fromscoreinscores
wherescore>80
orderbyscoredescending
selectscore;
●如上一个示例所述检索一个元素序列,但是将这些元素转换为具有新类型的对象。
例如,查询可以只从数据源中的某些客户记录检索姓氏。
或者,查询可以检索完整的记录,再使用它构建另一个内存中对象类型甚至XML数据,然后生成最终的结果序列。
下面的示例演示了从int到string的转换。
请注意highScoresQuery的新类型。
IEnumerable
fromscoreinscores
wherescore>80
orderbyscoredescending
selectString.Format("Thescoreis{0}",score);
●检索有关源数据的单一值。
IEnumerable
fromscoreinscores
wherescore>80
selectscore;
intscoreCount=highScoresQuery3.Count();
3、查询表达式
“查询表达式”是用查询语法表示的查询,是一流的语言构造。
它就像任何其他表达式一样,并且可以用在C#表达式有效的任何上下文中。
查询表达式由一组用类似于SQL或XQuery的声明性语法编写的子句组成。
每个子句又包含一个或多个C#表达式,而这些表达式本身又可能是查询表达式或包含查询表达式。
查询表达式必须以from子句开头,并且必须以select或group子句结尾。
在第一个from子句和最后一个select或group子句之间,查询表达式可以包含一个或多个下列可选子句:
where、orderby、join、let甚至附加的from子句。
还可以使用into关键字使join或group子句的结果能够充当同一查询表达式中附加查询子句的源。
3.1查询变量
在LINQ中,查询变量是任何存储查询而不是查询结果的变量。
更具体地说,查询变量始终是一个可枚举的类型,当在foreach语句中或对其IEnumerator.MoveNext方法的直接调用中循环访问它时,它将产生一个元素序列。
3.2查询变量的显式类型化和隐式类型化
使用var关键字指示编译器在编译时推断查询变量(或任何其他本地变量)的类型。
3.3开始查询表达式
查询表达式必须以from子句开头。
它同时指定了数据源和范围变量。
在对源序列进行遍历的过程中,范围变量表示源序列中的每个后续元素。
将根据数据源中元素的类型对范围变量进行强类型化。
在使用分号或延续子句退出查询之前,范围变量将一直位于范围中。
查询表达式可以包含多个from子句。
当源序列中的每个元素本身就是集合或包含集合时,可使用附加的from子句。
3.4结束查询表达式
查询表达式必须以select子句或group子句结尾。
group子句
使用group子句可产生按照指定的键组织的组序列。
键可以采用任何数据类型。
select子句
使用select子句可产生所有其他类型的序列。
into延续
可以在select或group子句中使用into关键字来创建用于存储查询的临时标识符。
在下面的示例中,以一千万人口范围为界对countries进行分组。
在创建这些组之后,使用附加子句筛选掉某些组,然后按升序对剩下的组进行排序。
若要执行这些附加操作,需要使用由countryGroup表示的延续。
varpercentileQuery=
fromcountryincountries
letpercentile=(int)country.Population/10000000
groupcountrybypercentileintocountryGroup
wherecountryGroup.Key>=20
orderbycountryGroup.Key
selectcountryGroup;
3.5筛选、排序和联接
在from开始子句以及select或group结束子句之间,所有其他子句(where、join、orderby、from、let)都是可选的。
任何可选子句都可以在查询正文中使用零次或多次。
where子句
使用where子句可以根据一个或多个谓词表达式筛选掉源数据中的某些元素。
orderby子句
使用orderby子句可以按升序或降序对结果进行排序。
您还可以指定次要排序顺序。
join子句
使用join子句可以根据每个元素中指定键之间的相等比较,对一个数据源中的元素与另外一个数据源中的元素进行关联和/或组合。
在LINQ中,联接操作是针对其元素具有不同类型的对象序列执行的。
在联接两个序列之后,必须使用select或group语句指定要存储到输出序列中的元素。
还可以使用匿名类型将每组关联元素中的属性组合为输出序列的新类型。
let子句
使用let子句可以将表达式(如方法调用)的结果存储到新的范围变量中。
3.6查询表达式中的子查询
查询子句本身可能包含一个查询表达式,该查询表达式有时称为“子查询”。
每个子查询都以它自己的from子句开头,该子句不一定指向第一个from子句中的同一数据源。
varqueryGroupMax=
fromstudentinstudents
groupstudentbystudent.GradeLevelintostudentGroup
selectnew
{
Level=studentGroup.Key,
HighestScore=
(fromstudent2instudentGroup
selectstudent2.Scores.Average())
.Max()
};
注意:
在LINQ文档中,存储查询的变量在其名称中包含单词“query”,而存储实际结果的变量在其名称中不包含单词“query”。
4、BT.SaaS.UI.WidgetFramework中LINQ的使用
以BT.SaaS.UI.WidgetFramework.DataAccess的DatabaseHelper类为例:
定义连接字符串
publicconststringConnectionStringName="DashboardConnectionString";
定义ApplicationID
publicconststringApplicationID="fd639154-299a-4a9d-b273-69dc28eb6388";
publicreadonlystaticGuidApplicationGuid=newGuid(ApplicationID);
定义最大行数,防止查询大量结果导致服务器阻塞
publicconstintMAX_ROWS=1000;
publicstaticDashboardDataContextGetDashboardData()
{
returnGetDashboardData(true,MAX_ROWS);
}
重载GetDashboardData()方法
publicstaticDashboardDataContext2GetDashboardData(boolreadOnly,intmaxRowsAllowedToAffect)
{
读取数据库连接配置
vardb=newDashboardDataContext2(ConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString);
设置事物隔离级别为READUNCOMMITTED
if(readOnly)
{
db.Connection.Open();
db.ExecuteCommand("SETTRANSACTIONISOLATIONLEVELREADUNCOMMITTED;SETNOCOUNTON;SETROWCOUNT"+maxRowsAllowedToAffect);
}
设置事物隔离级别,防止在提交事务之前记录受其他用户的影响,避免了幻觉读
else
{
db.Connection.Open();
db.ExecuteCommand("SETTRANSACTIONISOLATIONLEVELSERIALIZABLE;SETROWCOUNT"+maxRowsAllowedToAffect);
}
返回db连接对象
returndb;
}
更新操作
publicstaticvoidUpdate
class
{
using(vardb=GetDashboardData(false,1))
{
detach(obj);
db.GetTable
update(obj);
db.SubmitChanges();
}
}
更新所有
publicstaticvoidUpdateAll
class
{
using(vardb=GetDashboardData(false,items.Count))
{
Table
foreach(Titeminitems)
{
detach(item);
table.Attach(item);
update(item);
}
db.SubmitChanges();
}
}
删除操作
publicstaticvoidDelete
class,new()
{
using(vardb=GetDashboardData(false,1))
{
Table
table.Attach(entity);
table.DeleteOnSubmit(entity);
db.SubmitChanges();
}
}
添加操作
publicstaticvoidInsert
class
{
using(vardb=GetDashboardData(false,1))
{
db.GetTable
db.SubmitChanges();
}
}
如何使用DatabaseHelper
示例1:
Insert方法
protectedoverrideActivityExecutionStatusExecute(ActivityExecutionContextexecutionContext)
{
using(vardb=DatabaseHelper.GetDashboardData())
{
Widgetw=db.Widgets.Single(a=>a.ID==WidgetId);
WidgetInstancewi=newWidgetInstance();
wi.Title=w.Name;
wi.PageId=PageId;
wi.CreatedDate=wi.LastUpdate=DateTime.Now;
wi.VersionNo=1;
wi.State=string.Empty;
wi.WidgetId=w.ID;
wi.Expanded=true;
wi.State=w.DefaultState;
DatabaseHelper.Insert
this.NewWidget=wi;
}
returnActivityExecutionStatus.Closed;
}
示例2:
Update方法
protectedoverrideActivityExecutionStatusExecute(ActivityExecutionContextexecutionContext)
{
DatabaseHelper.Update
(wi)=>wi.State=this.State);
returnActivityExecutionStatus.Closed;
}
5、LINQ用法示例。
以Northwind数据库为例,使用LINQtoSQL设计器设计以上定义的五个类(Product,Category,Customer,Order和OrderDetail)的时候,每个类中的属性都映射了相应数据库中表的列,每个类的实例则代表了数据库表中的一条记录。
另外,当定义数据模型时,LINQtoSQL设计器同样会创建一个自定义DataContext类,作为数据库查询和应用更新/变化的主要渠道。
以上数据模型中定义的DataContext类命名为NorthwindDataContext。
该类中包含了代表每个建模数据库表的属性。
5.1检索操作
使用LINQ语法表达式可以十分简单的使用NorthwindDataContext类来查询和检索数据库中的数据。
LINQtoSQL会在运行时自动的转换LINQ表达式到适当的SQL代码来执行。
例如,编写以下LINQ表达式来根据ProductName检索单个Product对象:
还可以使用LINQ表达式来检索所有不存在于OrderDetails中的,并且UnitPrice大于100的所以Product:
当执行查询和检索像Product实例这样的对象时,LINQtoSQL会自动保持对这些对象任何变化或更新的跟踪。
我们可以进行任意次数的查询,以及使用LINQtoSQL的DataContext类做出更新,而这些变化都会被全部跟踪。
注意:
LINQtoSQL的变化跟踪发生于调用者端而不是在数据库中。
这就意味着使用跟踪不会销耗任何数据库资源,也不需要在数据库中改变/安装任何组件模块。
5.2更新操作
当对从LINQtoSQL中检索的对象做出更改之后,我们可以选择调用DataContext上的SubmitChange()方法来应用变化返回到数据库。
这将会导致LINQtoSQL动态计算并执行适当的SQL代码来更新数据库。
例如,编写以下代码更新数据库中ProductName为“Chai”的Product上的UnitPrice和UnitsInStock:
在下面代码中我们来遍历OrderDetails.Count等于0,UnitPrice大于100的Product,并把它们的ReorderLevel属性设为0:
当在以上代码中调用northwind.SubmitChanges()方法时,LINQtoSQL会计算并执行一组适当的UPDATE代码模块来修改RecorderLevel属性已变化的Product。
注意,如果一个Product的属性没有通过属性指定而发生变化,则该对象不会被认为是发生变化的,并且LINQtoSQL也不会对于该对象执行更新回数据库的操作。
例如,如果“Chai”对象的UnitPrice仍旧是$2,UnitsInStock仍旧是4,当调用SubmitChange()时不会导致任何数据库UPDATE代码模块的执行。
相似的,在第二个例子中的那些符合条件的Product中只有RecorderLevel原来不是0的才会在SubmitChange()被调用时更新。
5.3通过关系关联更新
我们很简单的通过表之间的关系关联来对数据模型建模。
例如,可以把每个Product建模到一个Category中,每个Order包含多条OrderDetail明细,每条OrderDetail明细都关联着一个Product,并且每个Customer拥有一组相关联的Order。
LINQtoSQL能够让我们不论是在查询还是更新数据中都可以利用这些关系关联。
例如,编写以下代码来创建一个新Product,并关联到一个数据库中已存在的“Beverages”Category上:
注意如何添加Product对象到该Category的Product集合中。
这样就会指明这两个对象之间存在关系关联,并会导致LINQtoSQL在调用SubmitChange()时,自动维护两者之间的主/外键关系关联。
另一个LINQtoSQL有助于管理交叉表关系关联的例子,让我们看一下如何对一个现有的Customer创建一个新的Order。
在设置Order的OrderDate和RequireDate和Freight后,然后创建两个Customer订购的Product相关的OrderDetail,并添加到Order中,随后关联Order到Customer上,最后更新所有变化回数据库:
6、LINQ数据验证
LINQtoSQL为开发者提供了多种途径,可以十分简洁地把这些整合到数据模型中。
LINQtoSQL会确保一旦添加数据验证逻辑,就可以无论何时何地在都应用于数据模型上。
这就避免了在多处的重复定义,保证了数据模型的可维护性和代码整洁。
6.1框架验证
使用LINQtoSQL设计器定义数据模型类时,会被默认附加一些根据数据库表构架推断出的验证规则。
数据模型类中的属性的数据类型会与数据库构架的数据类型相匹配。
这就意味着如果试图指定一个boolean值到decimal,或试图隐式转换numeric类型都会导致编译错误。
如果数据库中的列可以为null,则通过LINQtoSQL设计器创建的在数据模型中的相应的属性会是一个Nullable类型。
如果试图给未标记为Nullable的属性指定null值,会自动引发异常。
相似地,LINQtoSQL也会确保数据库中的identity/unique列值的正确验证。
也可以使用LINQtoSQL设计器来覆写这些默认的构架驱动的验证设置,但是通过默认我们可以自动获得它们,而且不需要进行额外的工作。
LINQtoSQL同样会自动处理转义的SQL值,这样就避免SQL注入攻击了。
6.2自定义属性验证
但在应用中如对于下面的代码,从单纯的SQL构架方面来说是合法的:
为防止虚假的电话号码被添加进数据库,我们可以添加一个自定义属性验证规则到Customer数据模型类中。
使用局部类的特性,可以十分简单的添加规则来验证电话号码,只需要添加一个新的包含如下方法定义的局部类到我们的项目中:
6.3自定义实体对象
属性级别的验证对于验证数据模型类上独立的属性是十分有效的。
但有时却需要同时验证一个对象上的多个属性。
考虑这样一个场景,同时设置Order对象上的OrderDate和RequiredDate属性:
以上的代码从单纯的SQL构架方面来说是合法的——即使规定交货日期是订单下达日期的前一天是毫无意义的。
LINQ
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linq 教程
![提示](https://static.bdocx.com/images/bang_tan.gif)