[MVC]ASP.NET MVC案例教程(基于ASP.NET MVC beta)——第六篇:拦截器

mikel阅读(660)

摘要
      本文将对“MVC公告发布系统”的发布公告功能添加日志功能和异常处理功能,借此来讨论ASP.NET MVC中拦截器的使用方法。
一个小难题
      我们继续完善“MVC公告发布系统”,这次,我们的需求是对公告发布功能添加日志记录能力,即在发布公告前,记录一次,在公告发布成功后,再记录一次。然后还要使得其具备异常处理,即当业务组件出现问题时,跳转到相应的错误页面并显示相应提示。
      有人可能笑了,这有什么难的,在DoRelease这个Action的开始和结束处各加入相应日志功能不久结了。异常处理更不在话下,直接try…catch搞定。
      没错,以上方法确实行得通,但是存在以下两点问题:
      1.代码重复问题。很多日志处理代码和异常处理代码是很相似的,这样就导致了各个Action中存在大量重复代码。
      2.职责被破坏。不要忘了,我们的Controller仅仅是控制器,它应该只负责表示逻辑,而不应该被一大堆日志处理代码和try…catch块包围。我们要的Action,应该是干净的、工整的、仅包含表示逻辑的Action。
      以上两点,造成了我们系统中的坏味代码。那么,怎么解决这个问题呢?
从厨师到AOP
      先来想象一个场景:饭店里的高级厨师怎么工作?我们知道,他不用洗菜切菜、不用端着盘子送菜、如果发现手里牛肉变质了他更不用拿着牛肉去找肉店老板理论,他的工作很单一:炒菜。
      当原料送来后,有专门的顺菜切菜工进行洗菜、切菜,然后把处理好的菜送给厨师,厨师只管下锅炒,炒完了送菜自然也不必关心,因为有专门的服务员负责这事。如果发现牛肉变质了,它只管说一声,自然有相应的人处理这事。
      这个场景就是典型的AOP(面向切面编程)。厨师可以看成是业务组件,它有个方法就是“炒菜”,但是炒菜前要切菜,炒完了要有人送菜,可这不是厨师该关心 的事啊!于是我们的切菜工和服务员就相当于拦截器,其中切菜工在炒菜前拦截,进行切菜,服务员在炒菜后拦截,负责送菜。当然,我们还有个异常拦截器:处理 问题的人,就是那个当厨师发现肉变质了喊一声,就来处理的人。
      基于这个场景,我们看看这样有什么好处。首先是厨师职责单一了,他可以专注于自己的工作:炒菜,而不必理会不该自己关心的问题。而且“拦截器们”可以复用 的,例如一个抠门的老板完全可以找3个厨师但是只招一名服务员,反正一名服务员就可以给三名厨师端菜,这样,拦截器的复用使得代码重复不见了!
回来
      好的,现在回到我们的“MVC公告发布系统”。相信看了上面的场景,你的灵感一定来了:对啊,Action不就是厨师吗,如果我们可以将日志功能做成拦截 器,在DoRelease执行前先拦截一次完成记录日志功能,DoRelease执行后再拦截一次记录一次日志。最好还有个拦截器,在Action发生异 常的时候可以拦截处理(就像上文处理变质牛肉的人),不就搞定了吗。
      可是要怎么实现拦截Action呢?真是幸运之极,ASP.NET MVC框架中内置了这种机制!哈哈,我们赶快来做吧!
实现拦截器
      在ASP.NET MVC中,有三种拦截器:Action拦截器、Result拦截器和Exception拦截器。我要用到第一种和第三种。其实所谓的ASP.NET MVC拦截器,也没什么神秘的,就是一个普通的类而已。只不过需要继承FilterAttribute基类,Action拦截器还要实现IActionFilter接口,而Exception拦截器需要实现IExceptionFilter接口。
      我们先来看实现:让我们在Controllers目录下新建一个Filters目录,然后在Filters下新建两个类,一个叫LoggerFilter一个叫ExceptionFilter。首先是LoggerFilter的代码。
LoggerFilter.cs:

 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Web;
 5using System.Web.Mvc;
 6using System.Web.Mvc.Ajax;
 7
 8namespace MVCDemo.Controllers.Filters
 9{
10    public class LoggerFilter : FilterAttribute, IActionFilter
11    {
12        void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
13        {
14            filterContext.Controller.ViewData["ExecutingLogger"= "正要添加公告,已以写入日志!时间:" + DateTime.Now; 
15        }

16
17        void IActionFilter.OnActionExecuted(ActionExecutedContext filterContext)
18        {
19            filterContext.Controller.ViewData["ExecutedLogger"= "公告添加完成,已以写入日志!时间:" + DateTime.Now;
20        }

21    }

22}


      可以看到,这个类继承了FilterAttribute并实现了IActionFilter。其中关键是IActionFilter,它有两个方 法,OnActionExecuting在被拦截Action前执行,OnActionExecuted在被拦截Action后执行。两个方法都有一个参 数,虽然类型不同,但其实都是一个作用:被拦截Action的上下文。
      这个地方我得解释一下,你拦截器拦截了Action,在做处理时难免要用到被拦截Action相关的东西,例如在我们的例子中,就需要想被拦截 Action所在Controller的ViewData中添加内容,所以,拦截器方法有一个参数表示被拦截Action的上下文是顺理成章的事。
      下面再看ExceptionFilter这个拦截器,它是在Action出现异常时发挥作用的。
ExceptionFilter.cs:

 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Web;
 5using System.Web.Mvc;
 6using System.Web.Mvc.Ajax;
 7
 8namespace MVCDemo.Controllers.Filters
 9{
10    public class ExceptionFilter : FilterAttribute,IExceptionFilter
11    {
12        void IExceptionFilter.OnException(ExceptionContext filterContext)
13        {
14            filterContext.Controller.ViewData["ErrorMessage"= filterContext.Exception.Message;
15            filterContext.Result = new ViewResult()
16            {
17                ViewName = "Error",
18                ViewData = filterContext.Controller.ViewData,
19            }
;
20            filterContext.ExceptionHandled = true;
21        }

22    }

23}


      异常拦截器一样需要继承FilterAttribute,但是不要实现IActionFilter,而是要实现IExceptionFilter接口,这 个接口只有一个方法:OnException,顾名思义,当然是发生异常时被调用了。我们看看我让它做了什么:首先将异常信息 (ExceptionContext一样也是上下文,而其成员的Exception就是一个Exception类型的实例,就是被抛出的异常)记录到 ViewData相应的键值里,然后我们要呈现Error这个视图。
      注意!这里已经不是Controller里了,而是另一个类,所以当然不能调用View方法返回ViewResult实例了。我们只好新建一个ViewResult实例,并将其视图名设为Error,将上下文中的DataView传过去。
      最后那行filterContext.ExcepitonHandled = true;很重要,这行的意思是告诉系统,异常已经处理,不要再次处理了。
应用拦截器
      好了,拦截器建立完了,要怎么应用到相应的Action上呢?如果你使用过Spring,你一定对其AOP是实现之麻烦深有感触,如果你和我一样讨厌写各 种XML的话,你真是太幸福了。因为在ASP.NET MVC中,应用拦截器简直是轻松加愉快。只要将拦截器当做Attribute写在要应用此拦截器的Action上就行了。看代码。
AnnounceController.cs:

 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Web;
 5using System.Web.Mvc;
 6using System.Web.Mvc.Ajax;
 7using MVCDemo.Models;
 8using MVCDemo.Models.Interfaces;
 9using MVCDemo.Models.Entities;
10using MVCDemo.Controllers.Filters;
11
12namespace MVCDemo.Controllers
13{
14    public class AnnounceController : Controller
15    {
16        public ActionResult Release()
17        {
18            ICategoryService cServ = ServiceBuilder.BuildCategoryService();
19            List<CategoryInfo> categories = cServ.GetAll();
20            ViewData["Categories"= new SelectList(categories, "ID""Name");
21            return View("Release");
22        }

23
24        [LoggerFilter()]
25        [ExceptionFilter()]
26        public ActionResult DoRelease()
27        {
28            AnnounceInfo announce = new AnnounceInfo()
29            {
30                ID = 1,
31                Title = Request.Form["Title"],
32                Category = Int32.Parse(Request.Form["Category"]),
33                Content = Request.Form["Content"],
34            }
;
35
36            IAnnounceService aServ = ServiceBuilder.BuildAnnounceService();
37            aServ.Release(announce);
38
39            ViewData["Announce"= announce;
40
41            System.Threading.Thread.Sleep(2000);
42            ViewData["Time"= DateTime.Now;
43            System.Threading.Thread.Sleep(2000);
44
45            return View("ReleaseSucceed");
46        }

47    }

48}


      看到没有,只要在DoRelease上写这么两个Attribute,一切就完成了,至于什么时候该调用什么拦截器,都是框架帮你完成了。注意一点,为了 让我们看出拦截器的时序,我们在DoRelease中加了一点东西,就是加了一个ViewData["Time"],里面记录了执行此Action的时 间,因为日志拦截器在前后都会记录时间,我们通过比较时间就可以看出执行顺序了。至于那两个Sleep则是让效果更明显的,这行代码的意思是让程序在这里 延迟2秒。
      要执行这个程序,我们还要改一下ReleaseSucceed.aspx视图,其实就是加几个地方显示ViewData里相应的数据。
ReleaseSucceed.aspx:

 1<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ReleaseSucceed.aspx.cs" Inherits="MVCDemo.Views.Announce.ReleaseSucceed" %>
 2<%@ Import Namespace="MVCDemo.Models.Entities" %>
 3
 4<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 5
 6<html xmlns="http://www.w3.org/1999/xhtml" >
 7<head runat="server">
 8    <title></title>
 9</head>
10<body>
11    <% AnnounceInfo announce = ViewData["Announce"as AnnounceInfo; %>
12    <div>
13        <h1>MVC公告发布系统——发布公告成功</h1>
14        <dl>
15            <dt>ID:</dt>
16            <dd><%= announce.ID %></dd>
17            <dt>标题:</dt>
18            <dd><%= announce.Title %></dd>
19            <dt>类别ID:</dt>
20            <dd><%= announce.Category %></dd>
21            <dt>内容:</dt>
22            <dd><%= announce.Content %></dd>
23            <dt>发布时间:</dt>
24            <dd><%= ViewData["Time"%></dd>
25        </dl>
26        <p><%= ViewData["ExecutingLogger"%></p>
27        <p><%= ViewData["ExecutedLogger"%></p>
28    </div>
29</body>
30</html>


      现在可以提交一则公告看结果了:

 
      没有问题,拦截器方法顺利执行,而且从时间可以看出,OnActionExecuting先执行,Action执行,然后OnActionExecuted执行。
      下面我们来看看异常拦截器的效果。要触发异常拦截器,首先要抛出一个异常,所以,我们在业务逻辑组件做点手脚。将MockAnnounceServices的Release方法改成如下:

1/// <summary>
2/// 发布公告
3/// </summary>
4/// <param name="announce"></param>

5public void Release(AnnounceInfo announce)
6{
7    throw new Exception("发布公告失败了!原因?没有原因!我是业务组件,我说失败就失败!");
8    return;
9}

      另外,我们还要实现一个Error.aspx视图,这是在异常拦截器中定义的错误视图。我们将它新建在Views/Shared下就可以了。顺便说一下, 共用的视图一般放在Shared下,因为ASP.NET MVC的视图寻找机理是当与Controller同名目录下不存在时,就到Shared下看看有没有此视图。
Error.aspx:

 1<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Error.aspx.cs" Inherits="MVCDemo.Views.Shared.Error" %>
 2
 3<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 4
 5<html xmlns="http://www.w3.org/1999/xhtml" >
 6<head runat="server">
 7    <title></title>
 8</head>
 9<body>
10    <div>
11        <h1>系统发生异常</h1>
12        <%= ViewData["ErrorMessage"%>
13    </div>
14</body>
15</html>

      好了,现在我们再提交新公告,会返回如下结果:

 
      很明显,业务组件抛出了异常,但是我们的Action方法中并没有用try…catch处理,但是异常拦截器成功拦截了异常,并做了相应处理。
      我们再回过头看看,使用了拦截器后,代码是不是很清晰呢。
小结
      通过本文,朋友们应该可以掌握拦截器的基本使用以及使用它在表示层实现AOP了。下一篇作为本系列的终结篇,将对ASP.NET MVC做一个全面的讨论,并与Web Form模型进行一个比较,使朋友们看清其优势、劣势,从而更好的学习使用这个框架。

[SQLServer]SQL Server数据导入导出资料大全

mikel阅读(468)

大家打开这个链接可以看到很多数据库的连接方法。http://www.connectionstrings.com
这些数据库之间的数据交换就是这个贴子所要总结的内容。

(一)SQL Server之间
把远程数据库中的数据导入到本地数据库。
http://community.csdn.net/Expert/topic/5079/5079649.xml?temp=.7512018
http://community.csdn.net/Expert/topic/5011/5011278.xml?temp=.1658899
http://community.csdn.net/Expert/topic/4982/4982625.xml?temp=.8189966

把A库里的其中几个表和视图导入B库
http://community.csdn.net/Expert/topic/5042/5042648.xml?temp=.2556574

SQL2005的数据库导入sql2000中
http://community.csdn.net/Expert/topic/4954/4954820.xml?temp=.3408625
在SQL2005中 如何导入 SQL2000中的Analysis Service ?
http://community.csdn.net/Expert/topic/4713/4713032.xml?temp=2.752322E-02

英文版sql server2005中bcp导出中文乱码问题
http://community.csdn.net/Expert/topic/5024/5024662.xml?temp=.7404596

如何将一个由SQL保存出来的*.bas文件再导回到SQL中
http://community.csdn.net/Expert/topic/4944/4944630.xml?temp=.8225672

(二)SQL Server –> Access

将SQL数据导出至Access(SQL与Access同一台机器上)
http://community.csdn.net/Expert/topic/4919/4919937.xml?temp=.2461359
将SQL数据导出至Access(SQL与Access不在同一台机器上)
http://community.csdn.net/Expert/topic/4906/4906691.xml?temp=.1377222
http://community.csdn.net/Expert/topic/4917/4917015.xml?temp=.7591364

(三)Access –> SQL Server
http://faq.csdn.net/FAQUnfurl.aspx?id=214875

为什么access与SQLServer2000导入导出时,数据库结构变了,默认主键,自动编号全没了?
http://community.csdn.net/Expert/topic/4712/4712570.xml?temp=.3942224

(四)txt(记事本)–> SQL Server
http://community.csdn.net/Expert/topic/5045/5045602.xml?temp=.0278284
TXT文件导入数据库出现乱码
http://community.csdn.net/Expert/topic/4939/4939026.xml?temp=.4465296
关于上千份大文本存入SQLServer
http://community.csdn.net/Expert/topic/4973/4973969.xml?temp=.4392816

(五)SQL Server –> txt
http://community.csdn.net/Expert/topic/4906/4906039.xml?temp=.6642267
SQL导出TXT或HTML求助
http://community.csdn.net/Expert/topic/5031/5031048.xml?temp=1.302737E-02

(六)Excel –> SQL Server
http://community.csdn.net/Expert/topic/5002/5002120.xml?temp=.4375727
http://community.csdn.net/Expert/topic/5086/5086098.xml?temp=.6402094
http://community.csdn.net/Expert/topic/4940/4940738.xml?temp=.8665125
http://community.csdn.net/Expert/topic/4938/4938969.xml?temp=1.918972E-03
http://community.csdn.net/Expert/topic/4892/4892120.xml?temp=.1775324
使用Excel导入数据库时,遇到这么个奇怪的问题,当Excel的一列中的数据类型明显不同时就会直接eof,??
http://community.csdn.net/Expert/topic/4929/4929620.xml?temp=.2570154

(七)DBF –> SQL Server
http://community.csdn.net/Expert/topic/5070/5070623.xml?temp=6.957644E-02

其他问题

怎样把数据库中的题目经过vb 导到word
http://faq.csdn.net/FAQUnfurl.aspx?id=215243

使用作业来完成DTS操作。
http://community.csdn.net/Expert/topic/5046/5046974.xml?tep=.9785425

还可以看看这个链接
http://blog.csdn.net/dutguoyi/archive/2006/10/16/1337274.aspx

xml –> sql server
http://community.csdn.net/Expert/topic/5041/5041228.xml?temp=.8233911

 oracle –> sql server
http://community.csdn.net/Expert/topic/5019/5019542.xml?temp=2.604312E-02

http://support.microsoft.com/kb/320125

http://fufun.cn/show.aspx?id=dC8yMDAyMDUzMC8xNS83NjUxOTEuaHRtbA_x003D__x003D_

在用Bulk Insert导入数据时的日期时间格式问题,
原因:bulk insert 不能转换数据类型,如果时间格式不正确就会出错。
http://community.csdn.net/Expert/topic/5100/5100692.xml?temp=.0559656

Excel –> SQL Server
导入时添加额外列,
例如 EXECL表的结构为 名称,内容,类别,
     SQLserver表的结构为 名称,内容,类别,属性
http://community.csdn.net/Expert/topic/5105/5105613.xml?temp=.3277399

补上
Excel –> SQL Server
导入时列名调整补充
http://community.csdn.net/Expert/topic/5086/5086098.xml?temp=.5973932

SQL Server 2005 –> SQL Server 2000
http://dev.csdn.net/author/taito/840c28a03ec0432492db18a329125bb5.html
http://community.csdn.net/Expert/topic/4954/4954820.xml?temp=.495083

SQL Server 2005 –> SQL Server 2000
用sql2005的导入导出工具将所有SQL2005的表和数据导出到SQL2000下,但是所有的主键、索引、关系、默认值全部丢了。
http://community.csdn.net/Expert/topic/4905/4905590.xml?temp=6.176394E-02

MySql –> SQL Server
http://topic.csdn.net/t/20031203/09/2521118.html
http://topic.csdn.net/t/20030407/11/1627991.html

SQL Server –> DB2
http://www.21cnvip.com/csdn/default.aspx?q=20021105/10/1149089.html

大家可以在下面的链接来搜索CSDN技术社区文档,很大的技术宝藏啊。
http://search.csdn.net/search/null/1/dev/

SQL Server 2000 –> SQL Server 7.0
SQL2000数据导入到SQL7,那些设置为主键的没有了
http://community.csdn.net/Expert/topic/5004/5004254.xml?temp=.1570246

SQL Server 2005 –> SQL Server 2000
2005导出的脚本为什么在2000运行总是出错?
http://community.csdn.net/Expert/topic/4682/4682496.xml?temp=.6038324

SQL Server之间复制数据库
http://community.csdn.net/Expert/topic/4835/4835266.xml?temp=.7495233

SQL Server 2000 –> SQL Server 2000
DTS包的制作
http://community.csdn.net/Expert/topic/5006/5006594.xml?temp=.2426111

SQL Server 2005 –> Excel
数据长度为255,出错
http://community.csdn.net/Expert/topic/4951/4951804.xml?temp=.2144739

SQL Server 2005 –> SQL Server 2000
http://community.csdn.net/Expert/topic/5089/5089204.xml?temp=.4461328

SQL Server 2000 –> SQL Server 2000
导出表的所有结构、所有数据
http://community.csdn.net/Expert/topic/5092/5092752.xml?temp=.8595545

From: http://blog.csai.cn/user1/19065/archives/2006/8916.html

[XML]在C#.net中如何操作XML

mikel阅读(632)

C#.net中如何操作XML
需要添加的命名空间:
using System.Xml;

定义几个公共对象:
XmlDocument xmldoc ;
XmlNode xmlnode ;
XmlElement xmlelem ;

1,创建到服务器同名目录下的xml文件:

方法一:
xmldoc = new XmlDocument ( ) ;
//加入XML的声明段落,<?xml version="1.0" encoding="gb2312"?>
XmlDeclaration xmldecl;
 xmldecl = xmldoc.CreateXmlDeclaration("1.0","gb2312",null);
 xmldoc.AppendChild ( xmldecl);
//加入一个根元素
xmlelem = xmldoc.CreateElement ( "" , "Employees" , "" ) ;
xmldoc.AppendChild ( xmlelem ) ;
//加入另外一个元素
for(int i=1;i<3;i++)
{

XmlNode root=xmldoc.SelectSingleNode("Employees");//查找<Employees>
XmlElement xe1=xmldoc.CreateElement("Node");//创建一个<Node>节点
xe1.SetAttribute("genre","李赞红");//设置该节点genre属性
xe1.SetAttribute("ISBN","2-3631-4");//设置该节点ISBN属性

XmlElement xesub1=xmldoc.CreateElement("title");
xesub1.InnerText="CS从入门到精通";//设置文本节点
xe1.AppendChild(xesub1);//添加到<Node>节点中
XmlElement xesub2=xmldoc.CreateElement("author");
xesub2.InnerText="候捷";
xe1.AppendChild(xesub2);
XmlElement xesub3=xmldoc.CreateElement("price");
xesub3.InnerText="58.3";
xe1.AppendChild(xesub3);

root.AppendChild(xe1);//添加到<Employees>节点中
}
//保存创建好的XML文档
xmldoc.Save ( Server.MapPath("data.xml") ) ;

//////////////////////////////////////////////////////////////////////////////////////
结果:在同名目录下生成了名为data.xml的文件,内容如下,
<?xml version="1.0" encoding="gb2312"?>
<Employees>
  <Node genre="李赞红" ISBN="2-3631-4">
    <title>CS从入门到精通</title>
    <author>候捷</author>
    <price>58.3</price>
  </Node>
  <Node genre="李赞红" ISBN="2-3631-4">
    <title>CS从入门到精通</title>
    <author>候捷</author>
    <price>58.3</price>
  </Node>
</Employees>

方法二:
XmlTextWriter xmlWriter;
   string strFilename = Server.MapPath("data1.xml") ;

   xmlWriter = new XmlTextWriter(strFilename,Encoding.Default);//创建一个xml文档
   xmlWriter.Formatting = Formatting.Indented;
   xmlWriter.WriteStartDocument();
   xmlWriter.WriteStartElement("Employees");

   xmlWriter.WriteStartElement("Node");
   xmlWriter.WriteAttributeString("genre","李赞红");
   xmlWriter.WriteAttributeString("ISBN","2-3631-4");

   xmlWriter.WriteStartElement("title");
   xmlWriter.WriteString("CS从入门到精通");
   xmlWriter.WriteEndElement();

   xmlWriter.WriteStartElement("author");
   xmlWriter.WriteString("候捷");
   xmlWriter.WriteEndElement();

   xmlWriter.WriteStartElement("price");
   xmlWriter.WriteString("58.3");
   xmlWriter.WriteEndElement();

   xmlWriter.WriteEndElement();

   xmlWriter.Close();
//////////////////////////////////////////////////////////////////////////////////////
结果:
<?xml version="1.0" encoding="gb2312"?>
<Employees>
  <Node genre="李赞红" ISBN="2-3631-4">
    <title>CS从入门到精通</title>
    <author>候捷</author>
    <price>58.3</price>
  </Node>
</Employees>
2,添加一个结点:

XmlDocument xmlDoc=new XmlDocument();
xmlDoc.Load(Server.MapPath("data.xml"));
XmlNode root=xmlDoc.SelectSingleNode("Employees");//查找<Employees>
XmlElement xe1=xmlDoc.CreateElement("Node");//创建一个<Node>节点
xe1.SetAttribute("genre","张三");//设置该节点genre属性
xe1.SetAttribute("ISBN","1-1111-1");//设置该节点ISBN属性

XmlElement xesub1=xmlDoc.CreateElement("title");
xesub1.InnerText="C#入门帮助";//设置文本节点
xe1.AppendChild(xesub1);//添加到<Node>节点中
XmlElement xesub2=xmlDoc.CreateElement("author");
xesub2.InnerText="高手";
xe1.AppendChild(xesub2);
XmlElement xesub3=xmlDoc.CreateElement("price");
xesub3.InnerText="158.3";
xe1.AppendChild(xesub3);

root.AppendChild(xe1);//添加到<Employees>节点中
xmlDoc.Save ( Server.MapPath("data.xml") );

//////////////////////////////////////////////////////////////////////////////////////
结果:在xml原有的内容里添加了一个结点,内容如下,
<?xml version="1.0" encoding="gb2312"?>
<Employees>
  <Node genre="李赞红" ISBN="2-3631-4">
    <title>CS从入门到精通</title>
    <author>候捷</author>
    <price>58.3</price>
  </Node>
  <Node genre="李赞红" ISBN="2-3631-4">
    <title>CS从入门到精通</title>
    <author>候捷</author>
    <price>58.3</price>
  </Node>
  <Node genre="张三" ISBN="1-1111-1">
    <title>C#入门帮助</title>
    <author>高手</author>
    <price>158.3</price>
  </Node>
</Employees>

3,修改结点的值(属性和子结点):

XmlDocument xmlDoc=new XmlDocument();
xmlDoc.Load( Server.MapPath("data.xml") );

XmlNodeList nodeList=xmlDoc.SelectSingleNode("Employees").ChildNodes;//获取Employees节点的所有子节点

foreach(XmlNode xn in nodeList)//遍历所有子节点
{
XmlElement xe=(XmlElement)xn;//将子节点类型转换为XmlElement类型
if(xe.GetAttribute("genre")=="张三")//如果genre属性值为“张三”
{
xe.SetAttribute("genre","update张三");//则修改该属性为“update张三”

XmlNodeList nls=xe.ChildNodes;//继续获取xe子节点的所有子节点
foreach(XmlNode xn1 in nls)//遍历
{
XmlElement xe2=(XmlElement)xn1;//转换类型
if(xe2.Name=="author")//如果找到
{
xe2.InnerText="亚胜";//则修改
}
}
}
}
xmlDoc.Save( Server.MapPath("data.xml") );//保存。

//////////////////////////////////////////////////////////////////////////////////////
结果:将原来的所有结点的信息都修改了,xml的内容如下,
<?xml version="1.0" encoding="gb2312"?>
<Employees>
  <Node genre="李赞红" ISBN="2-3631-4">
    <title>CS从入门到精通</title>
    <author>候捷</author>
    <price>58.3</price>
  </Node>
  <Node genre="李赞红" ISBN="2-3631-4">
    <title>CS从入门到精通</title>
    <author>候捷</author>
    <price>58.3</price>
  </Node>
  <Node genre="update张三" ISBN="1-1111-1">
    <title>C#入门帮助</title>
    <author>亚胜</author>
    <price>158.3</price>
  </Node>
</Employees>

4,修改结点(添加结点的属性和添加结点的自结点):
XmlDocument xmlDoc=new XmlDocument();
xmlDoc.Load( Server.MapPath("data.xml") );

XmlNodeList nodeList=xmlDoc.SelectSingleNode("Employees").ChildNodes;//获取Employees节点的所有子节点

foreach(XmlNode xn in nodeList)
{
XmlElement xe=(XmlElement)xn;
xe.SetAttribute("test","111111");

XmlElement xesub=xmlDoc.CreateElement("flag");
xesub.InnerText="1";
xe.AppendChild(xesub);
}
xmlDoc.Save( Server.MapPath("data.xml") );

//////////////////////////////////////////////////////////////////////////////////////
结果:每个结点的属性都添加了一个,子结点也添加了一个,内容如下,
<?xml version="1.0" encoding="gb2312"?>
<Employees>
  <Node genre="李赞红" ISBN="2-3631-4" test="111111">
    <title>CS从入门到精通</title>
    <author>候捷</author>
    <price>58.3</price>
    <flag>1</flag>
  </Node>
  <Node genre="李赞红" ISBN="2-3631-4" test="111111">
    <title>CS从入门到精通</title>
    <author>候捷</author>
    <price>58.3</price>
    <flag>1</flag>
  </Node>
  <Node genre="update张三" ISBN="1-1111-1" test="111111">
    <title>C#入门帮助</title>
    <author>亚胜</author>
    <price>158.3</price>
    <flag>1</flag>
  </Node>
</Employees>

5,删除结点中的某一个属性:
XmlDocument xmlDoc=new XmlDocument();
xmlDoc.Load( Server.MapPath("data.xml") );
XmlNodeList xnl=xmlDoc.SelectSingleNode("Employees").ChildNodes;
foreach(XmlNode xn in xnl)
{
XmlElement xe=(XmlElement)xn;
xe.RemoveAttribute("genre");//删除genre属性

XmlNodeList nls=xe.ChildNodes;//继续获取xe子节点的所有子节点
foreach(XmlNode xn1 in nls)//遍历
{
XmlElement xe2=(XmlElement)xn1;//转换类型
if(xe2.Name=="flag")//如果找到
{
xe.RemoveChild(xe2);//则删除
}
}
}
xmlDoc.Save( Server.MapPath("data.xml") );

//////////////////////////////////////////////////////////////////////////////////////]
结果:删除了结点的一个属性和结点的一个子结点,内容如下,
<?xml version="1.0" encoding="gb2312"?>
<Employees>
  <Node ISBN="2-3631-4" test="111111">
    <title>CS从入门到精通</title>
    <author>候捷</author>
    <price>58.3</price>
  </Node>
  <Node ISBN="2-3631-4" test="111111">
    <title>CS从入门到精通</title>
    <author>候捷</author>
    <price>58.3</price>
  </Node>
  <Node ISBN="1-1111-1" test="111111">
    <title>C#入门帮助</title>
    <author>亚胜</author>
    <price>158.3</price>
  </Node>
</Employees>

6,删除结点:
XmlDocument xmlDoc=new XmlDocument();
xmlDoc.Load( Server.MapPath("data.xml") );
XmlNode root=xmlDoc.SelectSingleNode("Employees");
XmlNodeList xnl=xmlDoc.SelectSingleNode("Employees").ChildNodes;
for(int i=0;i<xnl.Count;i++)
{
XmlElement xe=(XmlElement)xnl.Item(i);
if(xe.GetAttribute("genre")=="张三")
{
root.RemoveChild(xe);
if(i<xnl.Count)i=i-1;
}
}
xmlDoc.Save( Server.MapPath("data.xml") );

//////////////////////////////////////////////////////////////////////////////////////]
结果:删除了符合条件的所有结点,原来的内容:

<?xml version="1.0" encoding="gb2312"?>
<Employees>
  <Node genre="李赞红" ISBN="2-3631-4">
    <title>CS从入门到精通</title>
    <author>候捷</author>
    <price>58.3</price>
  </Node>
  <Node genre="李赞红" ISBN="2-3631-4">
    <title>CS从入门到精通</title>
    <author>候捷</author>
    <price>58.3</price>
  </Node>
  <Node genre="张三" ISBN="1-1111-1">
    <title>C#入门帮助</title>
    <author>高手</author>
    <price>158.3</price>
  </Node>
  <Node genre="张三" ISBN="1-1111-1">
    <title>C#入门帮助</title>
    <author>高手</author>
    <price>158.3</price>
  </Node>
</Employees>

删除后的内容:
<?xml version="1.0" encoding="gb2312"?>
<Employees>
  <Node genre="李赞红" ISBN="2-3631-4">
    <title>CS从入门到精通</title>
    <author>候捷</author>
    <price>58.3</price>
  </Node>
  <Node genre="李赞红" ISBN="2-3631-4">
    <title>CS从入门到精通</title>
    <author>候捷</author>
    <price>58.3</price>
  </Node>
</Employees>

 7,按照文本文件读取xml

System.IO.StreamReader myFile =new 
System.IO.StreamReader(Server.MapPath("data.xml"),System.Text.Encoding.Default);
//注意System.Text.Encoding.Default
string myString = myFile.ReadToEnd();//myString是读出的字符串
myFile.Close();

[Lucene]信息检索的函数库Lucene的使用总结

mikel阅读(699)

简单介绍:
Lucene是一个信息检索的函数库(Library),利用它你可以为你的应用加上索引和搜索的功能.

Lucene的使用者不需要深入了解有关全文检索的知识,仅仅学会使用库中的一个类,你就为你的应用实现全文检索的功能.

不过千万别以为Lucene是一个google那样的搜索引擎,Lucene甚至不是一个应用程序,它仅仅是一个工具,一个Library.你也可以把它理解为一个将索引,搜索功能封装的很好的一套简单易用的API.利用这套API你可以做很多有关搜索的事情,而且很方便。
发展历史:参见《.NET 的 Lucene 》
详细使用帮助:http://www.lucene.com.cn/net.htm (包含了建立索引/对索引进行搜索/删除索引中的文档/更新索引中的文档/查询语句/多个条件查询)
90%的语法在上述使用帮助都可以找到答案。
上面的使用帮助对排序说明的比较少,补充如下:
可以通过 SortField 的构造参数,我们可以设置排序字段,排序条件,以及倒排。
Sort sort = new Sort(new SortField(FieldName, SortField.DOC, false));
IndexSearcher searcher = new IndexSearcher(reader);
Hits hits = searcher.Search(query, sort);
排序对搜索速度影响还是很大的,尽可能不要使用多个排序条件。
Lucene.Net的语言处理包中Lucene.Net.Analysis.Cn的Bug :http://www.cnblogs.com/dudu/archive…6/22/17783.aspx
如何联合两个索引查询 / 分布搜索:
我们可以使用 MultiReader 或 MultiSearcher 搜索多个索引库。
MultiReader reader = new MultiReader(new IndexReader[] { IndexReader.Open(@"c:\index"), IndexReader.Open(@"\\server\index") });
IndexSearcher searcher = new IndexSearcher(reader);
Hits hits = searcher.Search(query);

IndexSearcher searcher1 = new IndexSearcher(reader1);
IndexSearcher searcher2 = new IndexSearcher(reader2);
MultiSearcher searcher = new MultiSearcher(new Searchable[] { searcher1, searcher2 });
Hits hits = searcher.Search(query);
还可以使用 ParallelMultiSearcher 进行多线程并行搜索。
如何合并索引库?
将 directory1 合并到 directory2 中。
Directory directory1 = FSDirectory.GetDirectory("index1", false);
Directory directory2 = FSDirectory.GetDirectory("index2", false);
IndexWriter writer = new IndexWriter(directory2, analyzer, false);
writer.AddIndexes(new Directory[] { directory });
Console.WriteLine(writer.DocCount());
writer.Close();
关于IndexWriter和IndexReader
Add Optimize Merge操作都是由IndexWriter来做的
Delete则是通过IndexReader完成
文档有一句话“即同一时间只允许IndexWriterIndexReader打开同一份索引.不能允许两个同时打开一份索引

记住IndexReader并不是查询索引的时候要用的,从字面含有好像是读取,所以可以同时查询索引和写入索引。

我的使用Lucene.net成功案例:http://video.5913.com/

[Flex]利用HTTPService实现Flex与服务器端低数据量通信

mikel阅读(595)

        Flex作为企业级的开发平台,与服务器端的通信是至关重要的。传统的Flash与服务器端主要通过HTTP、WebService和Flash Remoting方式通信,Flex除了保留了Flash平台与服务端通信方式外增加了一系列的高级服务功能。Flex与服务端通信主要分为两大类:一类 是通过传统的HTTP方式发送请求来和服务器端进行交互,另外一类则是通过LCDS等与服务端进行通信。

      低数据量的通信我们一般会选择Http方式来进行通信。通过HTTP方式Flex可以与任意服务器端进行通信。通过HTTP与服务器端通信又可以通过 HTTP请求方式和WebService实现。前者使用异步的HTTP请求方式,使用方式与Ajax一致,而后者则是通过标准的WebService协议 与服务端进行通信。下面我们看下HTTPService方式的通信,查阅相关的帮助文档我们可以知道HTTPService一些常用的方法和属性:

属性

method:发送HTTP请求的方法

 url : 请求服务的位置

request:发送请求的参数

requestTimeout:请求超时的时间

useProxy:是否使用代理的别名服务

方法:

send:参数为object类型默认为null,返回值类型为AsyncToken,执行一个HTTPService的请求

下面我们通过一个获取新浪体育新闻汇总的示例来说明HTTPService的使用

 


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="sinaSport.send()">
<mx:Style>
    global
    {
        fontSize:14pt;
    }
</mx:Style>
    
<mx:HTTPService id="sinaSport" url="http://rss.sina.com.cn/roll/sports/hot_roll.xml" useProxy="false">
    
</mx:HTTPService>
    
<mx:Panel x="53" y="90" width="975" height="461" layout="absolute" title="新浪体育要闻汇总 ">
    
<mx:DataGrid id="sportContent" x="23.5" y="10" width="911.5" dataProvider="{sinaSport.lastResult.rss.channel.item}" height="175">
        
<mx:columns>
            
<mx:DataGridColumn  width="300" headerText="标题" dataField="title"/>
            
<mx:DataGridColumn  width="150" headerText="作者" dataField="author" />
            
<mx:DataGridColumn  width="150" headerText="日期" dataField="pubDate" />
        
</mx:columns>
    
</mx:DataGrid>
    
<mx:TextArea x="23" y="193" width="912" height="183" htmlText="{sportContent.selectedItem.description}"/>
 
<mx:LinkButton x="855" y="384" label="详细信息" click="navigateToURL(new URLRequest(sportContent.selectedItem.link))"/>
    
</mx:Panel>
    
</mx:Application>

示例运行效果如下图:

在上面的代码中通过 如下代码声明了HTTPService 数据的位置


<mx:HTTPService id="sinaSport" url="http://rss.sina.com.cn/roll/sports/hot_roll.xml" useProxy="false">
    
</mx:HTTPService>

 

通过


<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="sinaSport.send()">

进行数据的发送

服务器端返回的结果则是通过sinaSport.lastResult来引用最后通过DataGrid来显示数据。

         从这个简单的例子我们可以看出因为HTTPService不是标准协议,基于HTTPService的通信方式访问的数据格式是由服务端来制定的,所以在 使用中要求Flex端与服务端的数据处理方式和交换格式一致。基于HTTP协议访问一般都通过纯文本数据进行传输。Flex能够轻松的将之转换为 ActionScript对象。

[JQuery]JQuery1.3发布了

mikel阅读(619)

2009-1-14,JQuery 1.3发布正式版,尽管之前版本的jQ已经让我们感受到了诸多便利,但是1.3还是给了我们额外的惊喜。
大家可以参考JQuery对于这个版本发布的官方文档:http://docs.JQuery.com/Release:jQuery_1.3
在官方的文档里你可以看到更详细的数据,不过在本篇里你可以看到更细节的分析。
==Sizzle,独立的css选择器引擎==
jQuery在之前的版本里已经逐渐酝酿出css选择器模块的独立。因为Sizzle的独立性,jQuery和Dojo开发组也有意通过Sizzle来开展长期合作(也就是共享部分代码)。
Sizzle也兼容之前版本的jQuery,那些依赖于低版本的jQuery的代码并无重写之忧。
Sizzle在最常见的使用场景中,也有了不同程度的性能提高。(什么是最常见使用的场景呢?可以参考http://ejohn.org/blog/selectors-that-people-actually-use/)
==Live,持久化事件绑定==
通常我们在使用jQuery的过程中,新构造的DOMElement也需要重新完成其事件绑定的逻辑。而jQuery 1.3 里的live函数可以让我们用类似于css behavior的方式来为未来要产生的DOMElement绑定事件。(当然你要拿它当css用,也无妨)
随之对应的,解除这种持久化事件绑定的方式可以用die来完成。
在sizzle.js的源码开头的几行,我们可以看到实现的思路。
document.addEventListener("DOMAttrModified", invalidate, false);
document.addEventListener("DOMNodeInserted", invalidate, false);
document.addEventListener("DOMNodeRemoved", invalidate, false);
==Event对象重封装==
jQuery.Event的主要功能被封装。(jQ有分拆IPO的倾向)
==html注入重写==
性能大幅提高(about 6x faster overall)。
还提供了新用法$("<script/>") 以代替$(document.createElement("script"))
==Offset重写==
性能大幅提高(almost 3x jump)
==jQuery.support,浏览器识别==
jQuery.browser是用useragent来识别浏览器,新增的jQuery.support以js的功能来识别浏览器。
==更新==
jQuery UI and the Validation plugin会同步更新。
如果你有依赖低版本的jQuery的代码的话,请阅读potentially-breaking changes
==Downloading==
jQuery Minified (18kb Gzipped)
jQuery Regular (114kb)
也可以用 http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js for a quickly-loading jQuery
==不兼容的Changes==
[@attr]不再需要写@了。(个人感觉这是一个很坏的change)
使用trigger( event, [data] )触发的事件现在会造成冒泡。(言下之意就是以前不会,以前没在意这个细节)
ready()不会等待所有的.css文件下载完毕才执行。(页面上的css引用要写在js前面了)
isFunction被简化。
(原来 !!fn && typeof fn != "string" && !fn.nodeName && fn.constructor != Array && /^[\s[]?function/.test( fn + "" )
现在 toString.call(obj) === "[object Function]" ,有很多你想不到的东西的typeof会是"Function")
逗号选择器的顺序有可能改变(因为Browsers that support querySelectorAll (Safari, Firefox 3.1+, Opera 10+, IE 8+),而它们返回的顺序和jQuery现有逻辑不同)
trigger 和 triggerHandler 原来提供的data形参可以接受一个数组,现在必须是类似于options的对象
(原来按以下对象属性的数组也可以被接受,{type: type,target: elem,preventDefault: function(){},stopPropagation: function(){},timeStamp: now()})
"extra"函数被去除(原来就没有写进文档里),如果trigger 和 triggerHandler 里data参数里提供了多余的成员,如{fa:function(){},fb:function(){},fc:function(){},type: type,target: elem,preventDefault: function(){},stopPropagation: function(){},timeStamp: now()},如果该成员isFunction的话,也会随trigger 和 triggerHandler被执行。
interal jQuery.event.trigger不再返回最后一个hander的返回值,只返回true or false(需要的话可以用jQuery.Event 来作为返回值的句柄)
需要在标准模式下运行jQuery,因为有一些函数在quirks mode运行不正确(Safari下甚至如此重要的一些选择器函数都会出错)
==增强特性==
(FEATURES)
===Core===
New queueing methods which you can use to manage existing queues (such as the animation queue) or construct new ones to manage completely separate workflows.(不是很理解有什么个特法)
jQuery 实例对象新增了两个属性selector and .context 。(以唤回你在构造这些对象时的记忆,jQuery( expression, context ) )
===Selector===
新增支持 复杂的not表达式。如 $(":not(a, b)") and $(":not(div a)")
not表达式其实是反向选择器,老实说我觉得这是一个很重要的特性
===Attributes===
jQuery实例对象新增成员函数toggleClass( String class, Boolean switch )
===Traversing===
jQuery实例对象新增成员函数 .closest( selector ) 以定位位置最接近的符合selector的ancestor element(祖先元素)。(会递归匹配parentNode,我原以为parent( [expr] ) 是做这个用的,后来发现parent( [expr] )只是很直白的选出以expr构造出的jQuery实例对象或者jQuery实例对象this的parentNode)
jQuery实例对象成员函数is()可以支持复杂选择器
===Manipulation===
$("<script/>")相当于原来的$(document.createElement("script")),前面介绍过了
===CSS===
Offset 重写了,性能提高,前面介绍过了
===Events===
Live Events
jQuery Event Object
.trigger()冒泡
(前面都介绍过了)
===Effects===
.hide() and .show() 重写,性能提高。
animate更加平滑,因为原来只控制width, height,opacity,现在连margin and padding控制了
未提供duration参数的animate调用不会有渐变,而是直接被同步化(因为原来animate的渐变都是异步的)的设为最终值
.toggle( Boolean switch ) 所有类似于toggle的函数(如toggleClass)都增加了switch这个参数支持,把toggle类的对象分为正反两种状态的话(如采用和不 采用,显示和隐藏),if(switch)则为正状态,反之亦然。
参考下面的代码:
var flip = 0;
$("button").click(function () {
$("p").toggle( flip++ % 2 == 0 );
});
原来无switch参数的时候相当于总是( flip++ % 2 == 0 ),现在我们可以自己控制了。
这是一个很好的新增特性。
jQuery.fx.off=true用来禁止所有animate
===Ajax===
jQuery.ajax( options )
options新增了{Function(data, type) dataFilter},可以用来在jQuery.ajax返回data前再处理一下data(比如过滤)
options新增了{Function() xhr},以xhr返回值作为XMLHttpRequest对象。这是一个很有用的增强,在跨域ajax时,一种做法是制造一个假的XMLHttpRequest。
load( url, [data], [callback] )的参数data可以是String了
===Utilities===
新增了jQuery.isArray( obj )函数
===Internals===
不再使用ua来作为识别浏览器的判断
YUI Compressor作为默认jQuery压缩工具
jQuery不再使用.constructor and instanceof(Will help to give jQuery better cross-frame support.)
==TESTING==
brabra
==PERFORMANCE==
css选择器,delegate过滤器,DOM修改操作(尤其是插入),.offset(),.hide()/.show() 都获得了不同程度的性能改进

[Lucene]Annotated Lucene:第三节 索引文件结构(1)

mikel阅读(666)

Annotated Lucene(源码剖析中文版)

Annotated Lucene 作者:naven  


  

3.1      索引文件结构

  Lucene使用文件扩展名标识不同的索引文件,文件名标识不同版本或者代(generation)的索引片段(segment)。如.fnm文件存储域Fields名称及其属性,.fdt存储文档各项域数据,.fdx存储文档在fdt中的偏移位置即其索引文件,.frq存储文档中term位置数据,.tii文件存储term字典,.tis文件存储term频率数据,.prx存储term接近度数据,.nrm存储调节因子数据,另外segments_X文件存储当前最新索引片段的信息,其中X为其最新修改版本,segments.gen存储当前版本即X值,这些文件的详细介绍上节已说过了。

    下面的图描述了一个典型的lucene索引文件列表:

     

lucene-indexfiles.jpg

    

如果将它们的关系划成图则如下所示(该图来自网上,出处忘了):

 

image010.jpg

 

这些文件中存储数据的详细结构是怎样的呢,下面几个小节逐一介绍它们,熟悉它们的结构非常有助于优化Lucene的查询和索引效率和存储空间等。

    

         

3.2  每个Index包含的单个文件

下面几节介绍的文件存在于每个索引index中,并且只有一份。 

3.2.1 Segments文件

    

索引中活动(active)的Segments被存储在segment info文件中,segments_N,在索引中可能会包含一个或多个segments_N文件。然而,最大一代的那个文件(the one with largest generation)是活动的片断文件(这时更旧的segments_N文件依然存在(are present)是因为它们暂时(temporarily)还不能被删除,或者,一个writer正在处理提交请求(in the process of committing),或者一个用户定义的(customIndexDeletionPolicy正被使用)。这个文件按照名称列举每一个片断(lists each segment by name),详细描述分离的标准(seperate norm)和要删除的文件(deletion files),并且还包含了每一个片断的大小。

  

2.1版本来说,还有一个文件segments.gen。这个文件包含了该索引中当前生成的代(current generation)(segments_N中的_N)。这个文件仅用于一个后退处理(fallback)以防止(in case)当前代(current generation)不能被准确地(accurately)通过单独地目录文件列举(by directory listing alone)来确定(determened)(由于某些NFS客户端因为基于时间的目录(time-based directory)的缓存终止(cache expiration)而引起)。这个文件简单地包含了一个int32的版本头(version header)(SegmentInfos.FORMAT_LOCKLESS=-2),遵照代的记录(followed by the generation recorded)规则,对int64来说会写两次(write twice)。

    

版本 包含的项 数目 类型 描述
2.1之前版本 Format 1 Int32 Lucene1.4中为-1,而在Lucene 2.1中为-3SegmentsInfos.FORMAT_SINGLE_NORM_FILE
Version 1 Int64 统计在删除和添加文档时,索引被更改了多少次。
NameCounter 1 Int32 用于为新的片断文件生成新的名字。
SegCount 1 Int32 片断的数目
SegName SegCount String 片断的名字,用于所有构成片断索引的文件的文件名前缀。
SegSize SegCount Int32 包含在片断索引中的文档的数目。
2.1及之后版本 Format 1 Int32 Lucene 2.1Lucene 2.2中为-3SegmentsInfos.FORMAT_SINGLE_NORM_FILE
Version 1 Int64 同上
NameCounter 1 Int32 同上
SegCount 1 Int32 同上
SegName SegCount String 同上
SegSize SegCount Int32 同上
DelGen SegCount Int64 为分离的删除文件的代的数目(generation count of the separate deletes file),如果值为-1,表示没有分离的删除文件。如果值为0,表示这是一个2.1版本之前的片断,这时你必须检查文件是否存在_X.del这样的文件。任意大于0的值,表示有分离的删除文件,文件名为_X_N.del
HasSingleNormFile SegCount Int8 该值如果为1,表示Norm域(field)被写为一个单一连接的文件(single joined file)中(扩展名为.nrm),如果值为0,表示每一个fieldnorms被存储为分离的.fN文件中,参考下面的“标准化因素(Normalization Factors)”
NumField SegCount Int32 表示NormGen数组的大小,如果为-1表示没有NormGen被存储。
NormGen SegCount * NumField Int64 记录分离的标准文件(separate norm file)的代(generation),如果值为-1,表示没有normGens被存储,并且当片断文件是2.1之前版本生成的时,它们全部被假设为0assumed to be 0)。而当片断文件是2.1及更高版本生成的时,它们全部被假设为-1。这时这个代(generation)的意义与上面DelGen的意义一样。
IsCompoundFile SegCount Int8 记录是否该片断文件被写为一个复合的文件,如果值为-1表示它不是一个复合文件(compound file),如果为1则为一个复合文件。另外如果值为0,表示我们需要检查文件系统是否存在_X.cfs
2.3 Format 1 Int32 Lucene 2.3中为-4 (SegmentInfos.FORMAT_SHARED_DOC_STORE)
Version 1 Int64 同上
NameCounter 1 Int32 同上
SegCount 1 Int32 同上
SegName SegCount String 同上
SegSize SegCount Int32 同上
DelGen SegCount Int64 同上
DocStoreOffset 1 Int32 如果值为-1则该segment有自己的存储文档的fields数据和term vectors的文件,并且DocStoreSegment, DocStoreIsCompoundFile不会存储。在这种情况下,存储fields数据(*.fdt*.fdx文件)以及term vectors数据(*.tvf*.tvd*.tvx文件)的所有文件将存储在该segment下。另外,DocStoreSegment将存储那些拥有共享的文档存储文件的segmentDocStoreIsCompoundFile值为1如果segment存储为compound文件格式(如.cfx文件),并且DocStoreOffset值为那些共享文档存储文件中起始的文档编号,即该segment的文档开始的位置。在这种情况下,该segment不会存储自己的文档数据文件,而是与别的segment共享一个单一的数据文件集。
[DocStoreSegment] 1 String 如上
[DocStoreIsCompoundFile] 1 Int8 如上
HasSingleNormFile SegCount Int8 同上
NumField SegCount Int32 同上
NormGen SegCount * NumField Int64 同上
IsCompoundFile SegCount Int8 同上
2.4及以上 Format 1 Int32 Lucene 2.4中为-7 (SegmentInfos.FORMAT_HAS_PROX)
Version 1 Int64 同上
NameCounter 1 Int32 同上
SegCount 1 Int32 同上
SegName SegCount String 同上
SegSize SegCount Int32 同上
DelGen SegCount Int64 同上
DocStoreOffset 1 Int32 同上
[DocStoreSegment] 1 String 同上
[DocStoreIsCompoundFile] 1 Int8 同上
HasSingleNormFile SegCount Int8 同上
NumField SegCount Int32 同上
NormGen SegCount * NumField Int64 同上
IsCompoundFile SegCount Int8 同上
DeletionCount SegCount Int32 记录该segment中删除的文档数目
HasProx SegCount Int8 值为1表示该segment中至少一个fieldsomitTf设置为false,否则为0
Checksum 1 Int64 存储segments_N文件中直到checksum的所有字节的CRC32 checksum数据,用来校验打开的索引文件的完整性(integrity)。

   

3.2.2 Lock文件

    

写锁(write lock)文件名为“write.lock”,它缺省存储在索引目录中。如果锁目录(lock directory)与索引目录不一致,写锁将被命名为“XXXX-write.lock”,其中“XXXX”是一个唯一的前缀(unique prefix),来源于(derived from)索引目录的全路径(full path)。当这个写锁出现时,一个writer当前正在修改索引(添加或者清除文档)。这个写锁确保在一个时刻只有一个writer修改索引。

  

需要注意的是在2.1版本之前(prior to),Lucene还使用一个commit lock,这个锁在2.1版本里被删除了。

  

3.2.3 Deletable文件

    

Lucene 2.1版本之前,有一个“deletable”文件,包含了那些需要被删除文档的详细资料。在2.1版本后,一个writer会动态地(dynamically)计算哪些文件需要删除,因此,没有文件被写入文件系统。

           

3.2.4  Compound文件(.cfs

    

Lucene 1.4版本开始,compound文件格式成为缺省信息。这是一个简单的容器(container)来服务所有下一章节(next section)描述的文件(除了.del文件),格式如下:

  

   

版本 包含的项 数目 类型 描述
1.4之后版本 FileCount 1 VInt  
DataOffset FileCount Long  
FileName FileCount String  
FileData FileCount raw Raw文件数据是上面命名的所有单个的文件数据(the individual named above)。

   

结构如下图所示:

    

image012.gif

      

               Annotated Lucene 作者:naven 日期:2008-10-20

[lucene]Lucene.Net FAQ问题集锦

mikel阅读(844)

部分信息翻译自 Apache Lucene FAQ,请注意标题中 "(翻译)" 字样。
IndexWriter.SetUseCompoundFile(true) 有什么用?
在创建索引库时,会合并多个 Segments 文件到一个 .cfs 中。此方式有助于减少索引文件数量,减少同时打开的文件数量。
可以使用 CompoundFileReader 查看 .cfs 文件内容。

CompoundFileReader reader = new CompoundFileReader(FSDirectory.GetDirectory("y:\\index", false), "_1oa.cfs");
foreach (string filename in reader.List())
{
  Console.WriteLine(filename);
}

IOException "Too many open files" (翻译)
原因:
某些操作系统会限制同时打开的文件数量。
解决方法:
1. 使用 IndexWriter's setUseCompoundFile(true) 创建复合文件,减少索引文件数量。
2. 不要将 IndexWriter's mergeFactor 的值设置过大。尽管这能加快索引速度,但会增加同时打开的文件数量。
3. 如果在搜索时发生该错误,那么你最好调用 IndexWriter.Optimize() 优化你的索引库。
4. 确认你仅创建了一个 IndexSearcher 实例,并且在所有的搜索线程中共用。(原文:"Make sure you only open one IndexSearcher, and share it among all of the threads that are doing searches — this is safe, and it will minimize the number of files that are open concurently. " 晕~~~,究竟要怎么做? )
为什么搜索不到结果?(翻译)
可能原因:
1. 搜索字段没有被索引。
2. 索引库中的字段没有分词存储,无法和搜索词语进行局部匹配。
3. 搜索字段不存在。
4. 搜索字段名错误,注意字段名称区分大小写。建议对字段名进行常量定义。
5. 要搜索的词语是忽略词(StopWords)。
6. 索引(IndexWriter)和搜索(IndexSearcher)所使用的 Analyzer 不同。
7. 你所使用的 Analyzer 区分大小写。比如它使用了 LowerCaseFilter,而你输入的查询词和目标大小写不同。
8. 你索引的文档(Document)太大。Lucene 为避免造成内存不足(OutOfMemory),缺省仅索引前10000个词语(Term)。可以使用 IndexWriter.setMaxFieldLength() 调整。
9. 确认在搜索前,目标文档已经被添加到索引库。
10. 如果你使用了 QueryParser,它可能并没有按照你所设想的去分析 BooleanQuerySyntax。
如果还不行,那么:
1. 使用 Query.ToString() 查看究竟搜索了些什么。
2. 使用 Luke 看看你的索引库究竟有什么。
TooManyClauses Exception (翻译)
使 用 RangeQuery, PrefixQuery, WildcardQuery, FuzzyQuery 等类型时可能会引发该异常。比如我们使用 "ca*" 来搜索 "car" 和 "cars",由于搜索结果文档(Document)所包含的 Term 超出 Lucene 默认数量限制 (默认1024),则会引发 TooManyClauses 异常。解决方法:
1. 使用 Filter 替换引发异常的 Query,比如使用 RangeFilter 替换 RangeQuery 搜索 DateField 就不会引发 TooManyClauses 异常。你可以使用 ConstantScoreQuery 像 Query 那样执行 Filter。第一次使用 Filters 时速度要比 Queries 慢一点,但我们可以使用 CachingWrapperFilter 进行缓存。
2. 使用 BooleanQuery.setMaxClauseCount() 加大 Terms 数量,当然这会增加内存占用。使用 BooleanQuery.setMaxClauseCount(int.MaxValue) 会避开任何限制。
3. 还有一个解决方法是通过缩小字段数据精度来达到减少索引中 Terms 数量的目的。例如仅保存 DateField 中的 "yyyymmddHHMM"(可以使用 Lucene 1.9 版本中的 DateTools)。
通配符
Lucene 支持英文 "?" 和 "*" 通配符,但不能放在单词首位。
QueryParser 是线程安全的吗?
不是。
MaxDoc() 和 DocCount()、NumDocs() 有什么不同?
MaxDocs() 表示索引库中最大的 Document ID 号,由于中间的某些 Document 可能被删除,因此不能使用 MaxDocs() 来表示 Document 数量。IndexWriter.DocCount()、IndexReader.NumDocs()、 IndexSearcher.Reader.NumDocs() 都表示索引库中 Document 数量。
为什么同时进行搜索和更新会引发 FileNotFoundException 异常?(翻译)
可能原因:
1. 某个搜索或更新对象禁用了锁。
2. 搜索和更新使用了不同的 lockDir。
3. 索引库被存放在 NFS (or Samba) 文件系统上。
尽管搜索是只读操作,但 IndexSeacher 为了获取索引文件列表,也必须打开时锁定索引库。如果锁没有正确设置,那么它将取回一个错误的文件列表(此时 IndexWriter 可能正在添加或优化索引),从而导致该异常发生。
索引文件有大小限制吗?(翻译)
某些 32 位操作系统限制每个文件不能大于 2GB。
解决方法:
1. 使用 IndexWriter.setMaxMergeDocs() 减小 MaxMergeDocs 数值。
2. 使用多个索引库。
什么是 Segments ?(翻译)
索引库中每个索引文件都是由多个 Segments 组成。当你添加一个 Document 到索引库,那么就可能会创建一个新的 Segment。你可以使用 Optimize() 来压缩索引库以减少 Segments 数量。
write.lock 有什么用?哪些类会用到它?(翻译)
write.lock 用来协调索引库的并发修改处理。
当 IndexWriter 打开索引库,或者 IndexReader 删除文档时都将创建该锁。
commit.lock 文件有什么用?哪些类会用到它?(翻译)
commit.lock 在调整索引库 segments 文件内容时使用。 IndexReader 和 IndexWriter 都会使用到它。
"Lock obtain timed out." 错误。在哪删除锁文件?(翻译)
一 般存放在系统临时目录(System.IO.Path.GetTempPath()),也可以在 app.config/web.config 中手工设置。可以手工进行删除,或者使用 "IndexReader.isLocked"、"IndexReader.unlock" 进行自动判断和删除操作。
FSDirectory.cs

public static readonly System.String LOCK_DIR = SupportClass.AppSettings.Get("Lucene.Net.lockDir", System.IO.Path.GetTempPath());

app.config / web.config

<configuration>
  <appSettings>
    <add key="Lucene.Net.lockdir" value="c:\index" />
  </appSettings>
</configuration>

如何更新已经索引的文档? (翻译)
你只能先删除,然后添加更新后的文档。
使用 IndexWriter.addIndexes(IndexReader[]) 和 IndexWriter.addIndexes(Directory[]) 合并索引库有什么不同? (翻译)
使用 Directory[] 参数所需的文件句柄和内存较小,索引文件仅需打开一次,而使用 IndexReader[] 参数则需要打开所有的索引库。