基础【LINQ】-------.NET深入体验与实战精要(摘抄)
1,LINQ
对不同数据额编程查询因数据源的不同而大相径庭,LINQ(Language Integrated Query)是一种非用来进行数据访问额编程模型,使得.NET语言可以直接支持数据查询。LINQ可以用相同的语法访问不同的数据源,无论是访问文件,XML,数据库,注册表,事件日子,活动目录,还是第三方如Flickr的数据,都可以将数据作为对象使用,能够更好地与编程模型集成。LINQ的目标是降低访问数据的复杂度。
借助于LINQ技术,我们可以使用一种类似鱼SQL的语法来查询任何形式的数据。
2,LINQ的架构
LINQ包括5个部分:LINQ to Objects , LINQ to DataSets , LINQ to SQL , LINQ to Entities 和 LINQ to XMl
其中,LINQ to SQL全称是基于关系数据的.NET语言集成查询,用于以对象形式管理管理数据,并提供丰富的查询功能。其建立于公共语言类型系统中额基于SQL的模式定义的集成之上,当保持关系模型表达能力对底层存储的直接查询的评测的性能时,这个集成了在关系型数据之上提供强类型。
LINQ to XML在System.XML.LINQ命名空间下实现对XML的操作,采用高效,易用,内存中的XML工具在宿主编程语言中提供XPath/XQuery功能。
3,传统的查询
使用传统的查询方式,查询不同的数据源,如查询:数据库,对象列表,XML等数据源。
(1)数据库查询
在传统方式下,要访问数据库,需要命名空间Systeem.Data.SqlClient中的各个类。
1 string connectionString="data source=127.0.0.1;Database=codematic;user id=sa;password=Int"; 2 using(SqlConnection myConnection=new SqlConnection(connectionString)) 3 { 4 SqlCommand cmd=myConnection.CreateCommand(); 5 6 cmd.CommandText="SELECT ID,Name From PROM_PRODUCT order by ID"; 7 8 myConnection.Open(); 9 10 using(SqlDataReader dr=cmd.ExecuteReader()) 11 { 12 while(dr.Read()) 13 { 14 Reponse.Write(dr.GetInt32(0).ToString()+","+dr.GetString(1)+"<gr>"); 15 } 16 17 } 18 }
简单的字符串表示,没有编译时类型检查,不知道这个字段是否存在,没有IDE的智能感知支持。
(2)对象列表查询
通过对象的属性,在一个对象的数组中查询符合条件的对象,这里主要用了泛型List<>,这个类的FindAll方法接收以委托方法,该委托方法实现为一个匿名方法,只返回性别是男的对象。
1 /// <summary> 2 /// 示例对象 3 /// </summary> 4 public class Man 5 { 6 private string name; 7 private string sex; 8 /// <summary> 9 /// 姓名 10 /// </summary> 11 public string Name 12 { 13 set { name = value; } 14 get { return name; } 15 16 } 17 18 public string Sex 19 { 20 set { sex = value; } 21 get { return sex; } 22 } 23 24 public Man(string Name, string Sex) 25 { 26 name = Name; 27 sex = Sex; 28 } 29 }
对如上的对象列表进行操作:
1 //构造对象列表 2 List<Man> manlist = new List<Man>(); 3 4 manlist.Add(new Man("李天平","男")); 5 manlist.Add(new Man("李嘉诚", "男")); 6 manlist.Add(new Man("徐静蕾", "女")); 7 //对象查询 8 List<Man> mans = manlist.FindAll( 9 10 delegate(Man man) 11 { 12 return man.Sex == "男"; 13 } 14 ); 15 16 //对象遍历输出 17 foreach (Man man in mans) 18 { 19 Response.Write(man.Name+","+man.Sex+"<br>"); 20 }
(3)XML查询
当数据存储在XML中的时候,我们可以从XML中查询数据。我们可以使用XPath表达式通过XmlDocument加载XML数据,通过SelectNodes查询匹配的节点数据。
1 System.Xml.XmlDocument doc = new System.Xml.XmlDocument(); 2 //装入指定的XML文档 3 string path = Server.MapPath("sampledata.xml"); 4 5 doc.Load(path); 6 System.Xml.XmlNodeList stulist = doc.SelectNodes("//学生");//选择匹配节点 7 foreach (System.Xml.XmlNode stu in stulist) 8 { 9 Response.Write(stu["姓名"].InnerText+","+stu["性别"].InnerText+"<br>"); 10 } 11
4,LINQ查询实例
LINQ定义了统一的语法可以从不同数据源或数据格式中获取数据,从而减少了开发人员学习的负担,减少了工作量,在LINQ查询中,不管什么数据源,都可以看做一个对象,你可以使用同样的代码在XML文件,SQL数据库,ADO.NET数据集,.NET集合类型,其他数据源或数据格式中查询和传输数据。
所有LINQ查询操作都由以下3个不同的操作组成;
* 获取数据源。
* 创建查询
* 执行查询
(1)数据库查询
LINQ to SQL 使用LINQ查询来访问数据库,要查询数据库,必须对对象类和数据库中的表建立映射。指定哪个属性映射为哪个字段列。为了实现映射,LINQ提供了DataContext类。
DataContext类型(数据上下文)是System.Data.Linq命名空间下的重要类型,用于把查询语句翻译成SQL语句,以及把数据从数据库返回给调用方和把实体额修改写入数据库。
DataContext提供了以下一些使用功能:
* 以日志形式记录DataContext生成的SQL。
* 执行SQL(包括查询和更新语句)。
* 创建和删除数据库。
DataContext是实体和数据库之间的桥梁,那么首先我们需要定义映射到数据表的实体。
1 [Table(Name = "p_Product")] 2 public class Product 3 { 4 [Column(IsPrimaryKey = true)] 5 public int ID { get; set; } 6 7 [Column(Name ="Name")] 8 public string Name { get; set; } 9 10 [Column(Name ="Descn")] 11 public string Descn{ get; set; } 12 13 [Column(Name ="Price")] 14 public decimal Price{ get; set; } 15 16 }
上述Product类被映射成一个表,对应数据库中的P_Product表,然后在类型中定义了4个属性,对应表中的4个字段。其中,ID字段是主键,如果没有指定Column特性的Name属性,那么系统会把属性名作为数据表的字段名,也就是说实体类的属性名就需要和数据表中的字段名一致,并且每个映射属性和字段的类型必须保持一致。
具体LINQ查询:
1 //获取数据源 2 string connectionString = "data source=127.0.0.1;Database=codematic;user id=sa;password="; 3 DataContext db = new DataContext(connectionString); 4 Table<Product> prolist = db.GetTable<Product>(); 5 6 //创建查询 7 var pros = from p in prolist 8 where p.Price > 1 9 orderby p.ID 10 select p; 11 //执行查询 12 foreach(Product pro in pros) 13 { 14 15 Response.Write(pro.Name+","+pro.Price.ToString()+"<br>"); 16 } 17 //GridView1.DataSource=pros; 18
通过GetTable方法获取表示底层数据表额Table类型,显然,数据库中的P_Product表的实体是Product类型。随后的查询句法,即使不懂SQL也可以看明白。从P_Product表中找出Price大于1的记录。
其中,var关键字是C#3.0的新特性,称为隐含类型,这个声明和javascripta的var类似,但也有不同。
* 相同点:它可以用var来声明任何类型的局部变量。
* 不同点:它仅仅负责告诉编译器,该变量需要根据初始化表达式来推断变量的类型,而且只能是局部变量。同时,既然让编译器推断类型必须在声明的时候赋值,所以不能是null值。
From ,where ,select 关键字就是LINQ的查询语法,该句法能在代码里表达查询时增进可读性和简洁性。
(2)对象列表
对于对象列表的数据,LINQ中查询方式如下:
1 //构造对象列表 2 List<Man> manlist = new List<Man>(); 3 4 manlist.Add(new Man("李天平","男")); 5 manlist.Add(new Man("李嘉诚", "男")); 6 manlist.Add(new Man("徐静蕾", "女")); 7 //创建查询 8 9 var mans=from r in manlist 10 where r.Sex =="男" 11 orderby r.Name descending 12 select r; 13 14 //执行查询 15 16 foreach(Man man in mans) 17 { 18 Response.Write(man.Name+","+man.Sex+"<br>"); 19 }
通过from声明一个变量,在where子句中过滤条件,orderby进行排序,select执行。通过类似于SQL语句的方式按对象进行查询。
(3)XML查询
LINQ to XML是使用LINQ查询和过滤XML数据的技术。它在System.Xml.Linq命名空间中定义了基于元素的新XML类,而System.Xml类是基于文档的。
1 //获取数据源 2 XmlDocument doc = new XmlDocument(); 3 string path = Server.MapPath("sampledata.xml"); 4 XElement stulist = XElement.Load(path); 5 6 //创建查询 7 8 var stus =from r in stulist.Elements("学生") 9 where r.Element("性别").Value=="男" 10 orderby r.Element("姓名") 11 select r; 12 13 foreach(XElement node in stus) 14 { 15 Response.Write(node); 16 }
我们再回头对比一下上面3种方式的查询,基本都包括3步操作: 获取数据源,创建查询,执行查询。而除了获取数据源后面2步的操作语法几乎相同,充分体现了LINQ的优势。
1)获取数据源
首先回头看看前面讲的LINQ的操作,可以知道LINQ大部分的操作对象都是实现了IEnumerable<T>接口或者IQueryable<T>的对象序列。上面写的代码实现示例中的数据源所用的数据类型是数组类型,而数组类型默认是支持泛型IEnumerable<T>接口。这样才使得LINQ在执行查询操作时可以foreach来实现对数据额迭代式访问。通常把支持泛型IEnumerable<T>接口或泛型IQueryable<T>接口的类型称为查询类型。数组类型正因为它是查询类型,在做LINQ查询数据源时才不用做任何的加工。
如果数据源是XML文档,则必须把它加工为支持泛型IEnumerble<T>接口的对象。在.NET Framwork类中XElement就属于查询类型,因此把XML文档转化成XElement类型对象即可。
1 using System.Xml.Linq; 2 3 XElement contacts=XElement.Load(@"c\mycontactlist.xml");
如果数据源是SQL数据库,则第一步先建立数据和实例间的关联映射。为实例对象写了查询语句后,LINQ会在运行时保持SQL与实例对象中的交互。在上面例子中,Product类型与数据库中的P_Product表对应,Table<Product>支持泛型IQuerable<T>接口。
2)创建查询
创建查询就是要把所需要的数据从数据源中取回来,并可在数据返回之前对数据进行分组,排序。创建查询,在C#中有新的语法定义。可以用它来创建查询表达式。表达式最少要包含两个子句:select 和 from 子句。
在上面那个从数组汇总返回偶数的例子代码中,查询表达式中有个子句: from ,where 和 select 。从表达式可以看出它跟SQL查询语句的区别,LINQ查询语句中子句的顺序是颠倒过来编写的。在LINQ中,查询表达式保存在变量中。如果查询被设计为返回一组数据,那么该变量就必须为查询类型变量。很重要的一点就是,该变量没有任何的操作也没有返回的信息,它只是保存了查询表达式的信息。查询表达式的设计人员必须要考虑好一个基本问题,那就是表达式执行后必须返回数据。
3)执行查询
执行查询又分为立即执行和延迟执行。
* 立即执行 : 当查询操作返回值为单个值的方法时,调用时它立即执行。
比如说返回值为单值的Cont , Max , Averag , First 方法调用时都会立即执行。它立即执行是因为这样的查询需要准备一个序列作为返回单值的计划接着上面从数组中返回偶数的例子,现在从中计算它返回偶数的数量,即是返回单值。
1 int query = (from num in numbers 2 where (num %2 )==0 3 select num ).Count();
如果要强制立即执行非返回值为单值的查询操作,就可以在查询表达式中调用ToList(of TSource) or ToArray(of TSource)方法,如下所示:
1 List<int > query2=(from num in numbrs 2 where (num%2)==0 3 select num).ToList(); 4 5 6 List<int > query3=(from num in numbrs 7 where (num%2)==0 8 select num).ToArray();
* 延迟执行 :以查询操作立即执行相比,延迟执行的查询操作,返回的值为一组数据而不是单个值。查询操作的执行延迟到foreach迭代循环过程中,经历整个迭代过程。
1 //创建查询,但这个时候并不执行 2 3 var pros = from p in prolist 4 where p.Price>1 5 orderby p.ID 6 select p; 7 8 //foreach遍历循环时才执行查询 9 10 foreach (Product pro in pros) 11 { 12 Response.Write(pro.Name+","+pro.Price.ToString(+"<br>"); 13 }
在这里,需要说明一下,查询表达式保存的变量没有任何操作和查询结果。从上面语句中可以看出,返回的数据都保存在foreach循环中的pro中。每次查询它只返回一个序列,查询表达式变量(pros)从不保存返回结果。这也说明了,无论在什么时候执行返回的结果都是最新版本的塑聚,不必担心脏数据的出现。当有几个应用程序共享一个数据源时,就不必担心执行LINQ查询获取的数据,另一个应用程序正在修改数据,所返回的数据是旧的版本了。
5,LINQ查询语法
如果想使LINQ运用到自己的项目开发中,则还需要花点时间研究一下LINQ的语法。尽管LINQ不只是能访问数据库,但是它在帮助程序员理解LINQ的价值时,首先是检验SQL 语句,然后再在代码中加入LINQ表达式完成同样的事情。下面的SQL 语句是在基于SQL Server的Codematic示例数据库中编写的,这个查询非常简单,列出价格大于1的产品。
select p.ID,p.Name,p.Descn,p.Price from P_Product p where p.Price>2 order by p.Name
现在来看看用LINQ表达式做同样的事情,解剖并弄清楚其中的细节,有2种查询语法:查询表达式和方法查询。目前,暂时先考虑查询表达式,下面的查询表达式将从GetCustomers()返回的结果查询IEnumerable类型,找出那些没有居住在Berlin的顾客,对于这个例子,你会认为是GetCustomers方法查询数据库并返回IEnumerable类型的。
1 var Productlist=from p in GetProducts() 2 where p.Price>1 3 orderby p.ContactName 4 select p;
目标数据 | var<变量>= | 使用类型推断来赋值 |
数据源 | from(项目)in (数据源) | 信息源提供一组项目 |
过滤器 | where (表达式) distinct | 表达式指定筛选的条件 |
排序 | orderby (表达式),(表达式)[升序][降序] | 控制结果的排序 |
计算 |
count(<表达式>),sum(<表达式>) ,min(<表达式>) max(<表达式>) ,avg(<表达式>) |
合计源项目组 |
输出 | select(表达式) | 输出内容 |