基础知识
1. LINQ的读法:(1)lin k (2)lin q
2. LINQ的关键词:from, select, in, where, group by, order by …
3. LINQ的注意点:必须以select或者是group by 结束。
4. LINQ的语义:
from 临时变量 in 集合对象或数据库对象
where 条件表达式
[order by条件]
select 临时变量中被查询的值
[group by 条件]
LINQ的查询返回值的类型是临时变量的类型,可能是一个对象也可能是一个集合。并且LINQ的查询表达式是在最近一次创建对象时才被编译的。LINQ的查询一般跟var关键字一起联用 (什么是var?匿名对象) 。以下的两个查询表达式是一样的效果:
var q = from name in methods
where (name.Name.Length > 15)
select name;
IEnumerable<MethodInfo> p = from name in methods
where (name.Name.Length > 15)
select name;
所以这样的我们就可以对LINQ的查询表达式进行嵌套查询:
var q = from t in
(from name in methods
where (!name.IsVirtual)
select name)
where (t.Name.Length > 15)
select t.Name;
5. LINQ的全称:Language-Integrated Query
6. LINQ的分类:LINQ to Object, LINQ to XML, LINQ to SQL, LINQ to ADO.NET
对于LINQ to SQL方面的文章网路上已经有很多了,我将不再讨论这方面的内容。下面我主要讨论的是LINQ to XML方面的内容,顺便提一下LINQ to Object。
LINQ to Object
MethodInfo[] methods = typeof(string).GetMethods();
var q = from t in methods
where (t.Name.Length > 15)
select t;
LINQ to Object主要是基于.NET框架里的Lambda表达式来实现的,因此上面的代码运行结果等同于下面的代码:
MethodInfo[] methods = typeof(string).GetMethods();
var q = methods
.Where((method) => method.Name.Length > 15)
.Select((name) => name.Name);
LINQ to XML
XElement xelem = XElement.Load(@"example.xml");
// 查询节点名为Item,并返回它们的PartNumber属性值的集合
IEnumerable<string> partNos = from item in xelem.Descendants("Item")
Select (string)item.Attribute("PartNumber");
foreach (string str in partNos)
Console.WriteLine(str);
在.NET3.5中,框架对XML的操作进行了扩展,这个扩展就是LINQ to XML。在名称空间System.Xml.LINQ下。从以上的代码我们可以看到增加了一个新的XElement对象。我们通过XElement.Load方法来装载XML文档,而不是传统的DOM模式XmlDocument.Load。
相比较于W3C的DOM文档结构来说,LINQ to XML为我们提供了一种更方便的创建一个XML文档的方式。
XElement contacts =
new XElement("Contacts",
new XElement("Name", "Ice Lee"),
new XElement("Phone", "010-876546",
new XAttribute("Type", "Home")),
new XElement("Phone", "425-23456",
new XAttribute("Type", "Work")),
new XElement("Address",
new XElement("Street", "ZhiXinCun"),
new XElement("City", "Beijin")
)
);
输出结果:
<? Xml version="1.0" encoding="utf-8"?>
<Contacts>
<Name>Ice Lee</Name>
<Phone Type="Home">010-876546</Phone>
<Phone Type="Work">425-23456</Phone>
<Address>
<Street>ZhiXinCun</Street>
<City>Beijing</City>
</Address>
</Contacts>
LINQ to XML提供了为丰富并且简洁的类来实现对XML的操作。相对于种类繁多的DOM模型的XML类库而言,LINQ的类使我们的学习曲线变得平滑并且还能达到相同的效果。LINQ to XML解决了DOM模型中的几个比较不方便的问题,如修改节点名字的问题;同时也抛弃了一些看起来很强大但是很不常用的东西,如实体和实体引用。这样使得LINQ to XML的操作速度更快并且更方便。以下的几个例子将展示给大家LINQ to XML如何完成节点名称修改,增加和删除的效果。
首先,我们看一下添加一个节点到XML中是这么样实现的:
XElement xelem = XElement.Load(@"example.xml");
XElement newXelem = new XElement("NewNode", "This is new node");
xelem.Add(newXelem);
相当的简单,只要先生成一个XElement对象然后把它Add到当前节点对象就可以了。进一步我们仔细查看一下XElement可以添加节点的方法。可以看到一共有Add, AddAfterSelf, AddAnnotation, AddBeforeSelf, AddFirst这五个方法。在默认情况下,Add的操作是将新节点作为被插入节点的最后一个孩子节点插入的,而AddFirst正好相反。AddAfterSelf和AddBeforeSelf则是将节点作为兄弟节点插入的,这里要注意的是调用这两个方法的时候不能以根节点作为被插入节点,因为XML文档规定只能有一个根节点。最后,我们来看一下AddAnnotation这个方法。AddAnnotation是为一个节点添加一个相关的评注的类对象。这个类对象可以用户自己定义,所以通过这个方法我们可以扩展XML文档对象的功能,例如根据节点来获取类对象的功能。下面是一段引用自msdn的代码:
public class MyAnnotation
{
private string tag;
public string Tag { get { return tag; } set { tag = value; } }
public MyAnnotation(string tag)
{
this.tag = tag;
}
}
… …
MyAnnotation ma = new MyAnnotation("T1");
XElement root = new XElement("Root", "content");
root.AddAnnotation(ma);
MyAnnotation ma2 = (MyAnnotation)root.Annotation<MyAnnotation>();
接下来我们再看一下如何利用LINQ to XML来更新XML的信息。对XML文档进行更新主要包括两个方面,一个方面是对元素属性和值得更新;另一方面是对元素名称的更新。在一般情况下,我们通常只对元素的属性和值进行更新,代码如下:
XElement xelem = XElement.Load(@"example.xml");
var partNos = from item in xelem.Descendants("Item")
where item.Attribute("PartNumber").Value == "872-AA"
select item;
foreach (XElement node in partNos)
{
node.Value = "Hello";
Console.WriteLine(node.Value);
}
对于XML元素的更新操作,关键就是在于查询上面,如何有效并准确的查询到目标元素是一个比较有挑战性的问题,这就像SQL语句一样对于同一个查询不同的写法和关系连接效率就不一样,这个读者需要自己多加练习。在上面的代码段中,我们去查找所有属性为PartNumber=872-AA的元素并将其更新为Hello。那么如果想操作元素值,只需将修改查询条件为:
var partNos = from item in xelem.Descendants("ProductName")
where item.Value == "Lawnmower"
XML文档的元素名称更新相比较于值更新要麻烦许多。由于XML文档结构是一个类树形结构,学过数据结构的读者知道要更新一个树节点的指针,最少需要三个步骤 :
A. 查找目标树节点的父节点
B. 将先前的节点的孩子转移到新节点
C. 将父节点的子节点替换(如果需要还要用算法重新排序树)。
所以我们也需要用同样的逻辑来处理XML节点的更新。那么相比较DOM文档对象和LINQ to XML对元素名称更新操作,LINQ to XML的步骤要简单许多。
XElement xel = XElement.Load(@"example.xml");
var itemNos = from item in xel.Descendants("Item")
select item;
int n = itemNos.Count();
for (int i=0; i<n; i++)
{
// 新创建节点
XElement nEl = new XElement("Element");
// 转移孩子节点
nEl.Add(itemNos.ElementAt(0).Elements());
// 替换
itemNos.ElementAt(0).ReplaceWith(nEl);
}
Console.Write(xel);
上面的代码是将所有名称为Item的元素替换成名称为Element。细心的读者可以看到我在for循环中获取itemNos的孩子都使用0这个索引值,为什么呢?这是因为在枚举器中如果前面的对象消失那么索引位置就会下移,那么当我们替换一个元素后,下一个元素的索引自动变为0,所以我们只要循环指定次数就可以遍历所有元素来。这也是为什么不用foreach的原因。那么我们进一步的出思考XML名称替换这个问题。我们会发现LINQ to XML的XElement类提供了4个方法用来支持该功能:ReplaceAll, ReplaceAttributes, ReplaceNodes和ReplaceWith。这四个方法除了ReplaceWith是操作本元素以为,其他的都是操作元素的孩子或是属性内容。这里提供的好处是如果我们想遍历替换操作,就不必去重复的查询目标元素。
最后,我们讨论一下如何利用LINQ to XML来删除一个元素。对于类树形结构的数据来说,删除一个元素意味着两种情况:一、删除本节点和其所有子节点;二、只删除本节点。而在删除节点之前,我们需要先定位到目标节点,所以要先进行查询操作:
XElement xelem = XElement.Load(@"example.xml");
var partNos = from item in xelem.Descendants("Item")
where item.Attributes("PartNumber").Single().Value == "872-AA"
select item;
partNos.Remove();
Console.Write(xelem);
对删除操作,LINQ to XML也一样提供了五个方法来操作:Remove, RemoveAll, RemoveAnnotations, RemoveAttributes, RemoveNodes。这五个方法跟添加和更新操作一样都是为了方便操作元素内容而存在的。
总结
从上面的示例代码中,我们可以看到其实使用LINQ to XML来操作XML对象是很方便的。而最主要,也是最重要的问题是你如何找到这个要操作的元素,也就是查询操作才是灵魂。LINQ的查询操作通常和匿名对象关键字var一起使用。匿名对象无疑是为LINQ而存在的,就像我们最前面的实例一样当查询返回的是集合对象或是新对象的时候,你需要先定义返回集合的类型,这很困难也很麻烦。这里再提一下LINQ的语法:
/ 返回一个关于Item的集合,该集合的变量名为item.
from item in xelem.Descendants("Item")
// 将上面的集合返回出去
select item;
对于所有的LINQ语句,总是从上层传递结果到下层去处理。所以当我们在考虑查询语句的时候,也要注意每一句的返回值。
本文的实例工程可以从以下网址获取:
http://www.cnblogs.com/Files/moonz-wu/MagicOfLinq.rar
在Virtual studio 2008 beta2测试通过。