65.9K
CodeProject 正在变化。 阅读更多。
Home

Aimee.NET - 重构 Lucene.NET:项目设置

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.98/5 (23投票s)

2010 年 11 月 19 日

Apache

8分钟阅读

viewsIcon

107186

downloadIcon

557

重构 Lucene.NET,使其遵循 .NET 的最佳实践和约定,而不是 Java 的编码风格和限制。一系列文章将在 www.codeproject.com 上发布,涵盖从头到尾的整个过程。

引言

这是关于重构 Lucene.NET 以遵循 .NET 最佳实践和约定,而不是 Java 的编码风格和限制的系列文章的第一篇。这些文章将在 The Code Project 上发布,涵盖从头到尾的整个过程。

我假设您熟悉 Visual Studio 2010,因此不会详细介绍如何设置项目、添加引用等任务。

本系列文章

  1. Aimee.NET - 重构 Lucene.NET:项目设置
  2. Aimee.NET - 更快的单元测试和重构文档文件夹

背景

我一直在研究使用 Lucene.NET 为 The Code Project 社区改进搜索体验的可能性。Lucene 库在索引和搜索内容方面是一个设计和实现都非常出色的组件。它非常易于使用,速度很快,并且提供出色的搜索结果。只是它的设计和实现没有使用 .NET 开发者熟悉的模式和类,因此获得的开发者支持不多。

最近,我读了一篇博客,Lucene.Net 需要你的帮助(否则它将消亡),警告说 Lucene.NET 项目正处于危险之中。这是因为该项目已有多月没有多少活动了。由于我目前正在将 Lucene.NET 整合到 The Code Project 中,我已经审查了源代码,并认为我理解了原因。

  • 首先,代码不遵循 .NET 的约定和最佳实践。
  • 代码几乎是 Java 源代码的直接复制。
  • 因此,它不使用属性,而是使用 get 和 set 方法。
  • 它不使用枚举,而是使用通过类模拟枚举的模式。
  • 单元测试花费的时间太长,使其对于 TDD 无效。
  • 代码没有利用 .NET Framework 的类库来提高性能和代码质量。
  • 内联文档不是 XML 文档格式。

因此,我征得了老板 Chris Maunder 的许可,花费一些时间为 .NET 开发者社区清理代码,并希望得到社区的帮助。

目的是生成一个更适合 .NET 开发者使用和改进的代码库。转换将包括 Lucene.NET SVN 仓库中 1032168 Revision 的所有文件。

项目目标

  1. 为 Code Project 社区提供最佳的搜索体验。
  2. 为社区提供一种途径,可以通过公开项目来贡献于此改进。
  3. 为 .NET 社区提供一个更好的 Lucene 库的 .NET 版本。

Using the Code

代码存储在 CodePlex。在重构的各个阶段,我以及希望的 CodeProject 社区成员团队将在“下载”标签下发布稳定版本。如果您想获取最新的开发中版本,所有签入都可以在“源代码管理”标签下找到。

项目启动

选择一个名字

此过程的第一步是为项目选择一个名字,因为 Lucene 是 Apache.org 的版权所有。

为了将名字保留在 Code Project 系列中,我选择了 **Aimee.NET**。Aimee 是我们销售和营销团队的成员之一。她来 The Code Project 的时间比我长。她曾担任前台、助理会计、广告运营,现在是她的当前职位。因此,这里 The Code Project 的所有人都认识她,并且她知道所有隐藏的“骨头”(隐喻,指事情的细节和秘密)。

创建 Codeplex 项目

CodePlex 上创建项目是一个非常简单的过程。只需访问 CodePlex 主页,然后点击“创建项目”按钮。这将引导您完成项目设置的步骤。由于我已经完成了这个步骤,我将不再一步一步地解释。

填充源代码控制存储库

我从官方 Lucene.NET SVN 仓库获取了 Lucene.NET 的副本。这包括 Lucene.NET、Demos、Contributed Extensions 和 Unit Tests 的源代码。下一步是将其签入 CodePlex 源代码控制。

CodePlex 使用 Team Foundation Server 2010 进行源代码和项目管理。在家,我使用 Visual Studio 2010 中的 TFS 客户端进行 CodePlex 开发。在工作中,我们使用 SVN。幸运的是,CodePlex 在访问源代码控制时同时支持这两种客户端,因此我在工作时不必切换配置。

经过几次尝试,我终于以一种令我满意的方式将源代码放入了存储库。一个小小的警告,不要在凌晨 3 点进行初步的签入。在进行操作之前,您需要仔细考虑应该将什么放在哪里。

转换为 .NET 4 和 VS 2010

Project Structure

一旦有了良好的起点,我就创建了一个 Visual Studio 2010 解决方案文件,并将各种 Lucene.NET 项目添加进去。这些项目是 Visual Studio 2005 项目,VS2010 会弹出转换向导来更新项目以适应 VS2010 格式。

要将每个项目转换为 .NET 4,我打开每个项目的属性页,并将“目标框架”设置为“.NET Framework 4”。

然后,我将项目之间的所有依赖关系更改为项目依赖项。

Lucene.NET 的单元测试有两个外部依赖项:SharpZipLibNUnit(版本 2.5.8)。我将它们放在一个“Lib”目录中,并添加了一个名为“Dependencies”的解决方案文件夹。所需的 DLL 已添加到此目录。然后,我将引用更改为使用该目录中的 DLL。

经过一些调整,解决方案可以编译了。我将其签回了。您可以在右侧看到这一点。

使测试运行起来

我使用两个测试运行器来执行单元测试:NUnit GUI 和 CodeRush 测试运行器。它们在执行测试时确定 AppDomain 基目录的方式存在差异。我修改了 `Test.nunit` 配置文件,将基目录设置为 `bin\debug` 目录,这使其与旧版本的 NUnit 和 CodeRush 测试运行器兼容。

此外,单元测试在 Windows 7 中与系统临时目录存在一些问题,所以我修改了

  • `TestBackwardsCompatibility.UnZip` 方法以显式使用 `AppDomain.BaseDirectory`
  • 并且 `_TestUtil.GetTempDir` 方法使用 `bin/debug` 目录下的一个“temp”目录。
  • 将所有 `System.IO.Path.GetTempPath` 的出现都更改为调用 `_TestUtil.GetTempDirName`,这是一个新方法,只返回“temp”。

我修复了 `Test_Search_FieldDoc` 中的一个 bug,该 bug 会在测试期间抛出异常时导致无限循环。

此外,`Index.TestTransactions.TestTransactions_Renamed` 没有终止。可能是它在等待测试创建的所有后台线程结束。我添加了一个 `[Ignore]` 属性,以便测试能够运行。我稍后会处理这个问题。

`Store.TestDirectory.TestDirectInstantiation` 在 `MMapDirectory` 的构造函数处中止。添加了一个 `[Ignore]` 属性,因为它会停止测试运行。我稍后会修复这个问题。这个问题也存在于 `Store.TestWindowsMMap.TestMmapIndex`。它也被设置为 `[Ignore]`。

同样,`Messages.TestNLS.???` 测试似乎失败了,因为我的区域设置为 `en-ca` 而不是 `en-us`。我稍后也会研究这个问题。

`Search.Function` 测试运行缓慢。快速审查表明,测试设置方法重新创建了一个应该在 Fixture 设置方法中创建的大型集合。

一旦所有测试都运行完毕,请将所有内容重新签入。我们现在有信心我们拥有一个可以编译并按预期运行的项目,除了少数小问题。

加快测试速度

现在我们有了一套单元测试,我们可以尝试让它们变得有用。目前,执行时间太长,无法用于 TDD 风格的工作,而这对于重构来说至关重要。我们需要在每个重构步骤中知道我们没有破坏任何功能。

在 NUnit GUI 中运行单元测试会显示以下状态。

Unit Test Status

在一台功能强大的机器上,这需要 45 分钟。这显然不适用于 TTD 风格的开发或重构,因此需要改进测试,并且必须选择一个有意义的子集,将总持续时间缩短到一分钟以内。

Conmputer Info

为了确定哪些测试需要性能改进,我将测试结果保存到 XML 文件中,您可以 在此处 获取。该文件包含每个测试和测试组的执行时间。我发现 LinqPad 是分析几乎任何来源数据的绝佳工具。在 LinqPad 中输入以下代码并执行代码,会生成两个表。第一个表是所有运行时间超过 5 秒的测试用例,按时间降序排序。第二个表是所有运行时间超过 10 秒的测试套件,按时间降序排序。

var doc = new XmlDocument();
doc.Load(@"C:\Users\matthew.CODEPROJECT\Documents\Work\Aimee.Net\Article1 - 
    Starting the Project\TestResult.xml");

//doc.SelectNodes("descendant::test-suite").Dump();
var testcases=doc.SelectNodes("descendant::test-case");

var testdata = (from XmlNode c in testcases
where c.Attributes["executed"].Value == "True"
select new {
    Suite = c.ParentNode.ParentNode.Attributes["name"].Value,
    Name = c.Attributes["name"].InnerText,
    Time = float.Parse(c.Attributes["time"].Value)})
.OrderByDescending (d => d.Time);

testdata.Where(td => td.Time >= 5).Dump();

testdata.GroupBy(td => td.Suite)
        .Select ( grp => new { Suite = grp.Key, Time = grp.Sum(td => td.Time)})
        .Where( ts => ts.Time >= 10)
        .OrderByDescending(Su=> Su.Time)
        .Dump();

Test Case Times

Test Suite Times

这使我能够查看持续时间最长的测试套件和用例。我将解决其中一些问题,并在下一篇文章中汇报我为实现一套允许 TDD 方法进行重构的测试所做的更改。

历史

  • 2010 年 11 月 18 日 首次发布
  • 2010 年 12 月 8 日 - 添加了系列中其他文章的链接
© . All rights reserved.