DbEntry 开发实践:Wiki 系统(二)
书接上文,我们有了一个非常简单的Show.aspx,现在我们需要一个Edit.aspx页面来进行Wiki的编辑。
新增一个Edit.aspx文件,这个页面同样接受一个title参数,这个页面上主要就是FckEditor,并且保存事件也由FckEditor提供(为了某些方便性,我修改了一些FckEditor控件的源代码,这个修改的细节就不详细说了,所以在这里请使用我提供的FckEditor控件),在这里,我们要使用这个Edit.aspx页面提供新增和修改两项功能,所以先给Article类增加一个函数SaveArticle用来处理这两种情况,不过,让我们先来写个单元测试吧。
写单元测试,可以帮助我们测试我们的实现,帮助阅读代码,协助重构等;而先写单元测试,可以在我们考虑其实现方式之前,先考虑其用法,所以可以帮助我们实现更好用的库。我这里仍然使用的是NUnit,而不是VS自带的单元测试库,因为我发现,(至少早期来说)VS的单元测试库对于数组的比较需要自己用foreach,而NUnit可以直接用AreEqual。所以我还是用NUnit,反正只需要带一个十几K的DLL就可以了……
恩,现在,我们有了单元测试,不过,我们还没有实现这个函数,现在我们就来实现它。如果你安装了Resharper,可以让它从单元测试的代码里,帮你建立这个函数的原型。我们实现的SaveArticle函数如下:
然后,我们运行单元测试,失败了,异常信息是:Lephone.Data.DbEntry的类型初始值设定项引发异常。恩,确实,我们的单元测试还没有配置数据源呢。
在VisualWiki.UnitTests中加入一个xml文件VisualWiki.config.xml,这个配置文件的配置项,基本上和VisualWiki中的Web.config一样,只是我这一次希望,其生成的SQL显示在单元测试的输出面板,而单元测试的输出面板直接接收Console的输出,另外,我希望Sqlite的数据库生成在系统的Temp目录下,修改后的VisualWiki.config.xml如下:
然后将VisualWiki.config.xml的编译方式设置为内嵌的资源,另外,我们也希望单元测试完成时删除数据库文件,所以修改TestArticle加入处理函数:
好,现在,运行单元测试,显示成功,点选单元测试项,我们还能在它的面板里看到在这个测试运行时,所生成的所有SQL语句。其实,使用Sqlite,还可以使用内存数据库来做单元测试,不过,内存数据库是连接型数据库,连接结束后,数据也就抛弃了,所以,需要我们让这个测试函数内的所有调用,都在一个连接里完成,在DbEntry里,可以用DbEntry.Context.UsingConnection来做,使用方法和UsingTransation类似。我以前以为内存数据库的速度更快,不过考虑后,我觉得,如果用于测试的数据库需要预先有N个表,每个表里还有一些测试数据的话,使用文件数据库,直接复制的方式,速度反而可能更快,DbEntry 500多个测试,有一半左右需要准备数据库,使用文件型数据库,所有单元测试运行完,只需要10秒左右。
现在,我们已经实现了SaveArticle函数,所以我们开始编写Edit.aspx页面,除了保存Wiki内容之外,我们还希望保存之后,直接返回Show.aspx页面,所以,修改好的Edit.aspx如下:
然后我们在VisualWiki的目录下建立fckeditor目录,把FckEditor的相关文件复制到这个目录下,现在,从Edit.aspx运行程序,显示一个异常:The Parameter title can't be empty。恩,在url后加上参数?title=test,然后回车,显示出FckEditor的编辑界面,只是高度有点儿小,随便输入一些内容,点击FckEditor上的保存按钮,页面返回Show.aspx,并且显示了刚才我们编辑的内容。把url中的Show修改为Edit,然后回车,我们又进入了编辑界面,这一次,编辑框里已经有了我们刚才编辑的内容,修改一下,再点击保存按钮,我们回到了Show页面,它也显示出了我们编辑后的内容。
现在,我们来编辑一下Main.master,用以提供一个Edit链接。母板页也需要title参数,所以先设置它从Lephone.Web.SmartMasterPageBase继承,以便使用HttpParameter,然后添加一个Edit的HyperLink控件,在Page_Load里对这个链接控件进行一些设置,修改后的Main.master如下:
再次从Show.aspx运行程序,添加参数?title=test,页面显示文章test,点击顶部的Edit链接,进入编辑页面,编辑之后,点击保存,回到显示页。
至此,VisualWiki项目已经可以进行可视化的新建、编辑操作,我们还需要进行很多的工作以便让它完成所有需求。不过现在,把它提交到SVN库里先。目前的源代码可以在这里下载:VisualWiki2.7z
未完待续……
新增一个Edit.aspx文件,这个页面同样接受一个title参数,这个页面上主要就是FckEditor,并且保存事件也由FckEditor提供(为了某些方便性,我修改了一些FckEditor控件的源代码,这个修改的细节就不详细说了,所以在这里请使用我提供的FckEditor控件),在这里,我们要使用这个Edit.aspx页面提供新增和修改两项功能,所以先给Article类增加一个函数SaveArticle用来处理这两种情况,不过,让我们先来写个单元测试吧。
using NUnit.Framework;
using VisualWiki.Models;
namespace VisualWiki.UnitTests
{
[TestFixture]
public class TestArticle
{
[Test]
public void TestSaveArticle()
{
Article.SaveArticle("title", "whatever it is");
var o1 = Article.FindByTitle("title");
Assert.IsNotNull(o1);
Assert.AreEqual("whatever it is", o1.Content);
Article.SaveArticle("title", "hello, I'm coming");
var o2 = Article.FindByTitle("title");
Assert.IsNotNull(o2);
Assert.AreEqual("hello, I'm coming", o2.Content);
}
}
}
namespace VisualWiki.UnitTests
{
[TestFixture]
public class TestArticle
{
[Test]
public void TestSaveArticle()
{
Article.SaveArticle("title", "whatever it is");
var o1 = Article.FindByTitle("title");
Assert.IsNotNull(o1);
Assert.AreEqual("whatever it is", o1.Content);
Article.SaveArticle("title", "hello, I'm coming");
var o2 = Article.FindByTitle("title");
Assert.IsNotNull(o2);
Assert.AreEqual("hello, I'm coming", o2.Content);
}
}
}
写单元测试,可以帮助我们测试我们的实现,帮助阅读代码,协助重构等;而先写单元测试,可以在我们考虑其实现方式之前,先考虑其用法,所以可以帮助我们实现更好用的库。我这里仍然使用的是NUnit,而不是VS自带的单元测试库,因为我发现,(至少早期来说)VS的单元测试库对于数组的比较需要自己用foreach,而NUnit可以直接用AreEqual。所以我还是用NUnit,反正只需要带一个十几K的DLL就可以了……
恩,现在,我们有了单元测试,不过,我们还没有实现这个函数,现在我们就来实现它。如果你安装了Resharper,可以让它从单元测试的代码里,帮你建立这个函数的原型。我们实现的SaveArticle函数如下:
public static void SaveArticle(string title, string content)
{
var o = FindByTitle(title);
if(o == null)
{
New.Init(title, content).Save();
}
else
{
o.Content = content;
o.Save();
}
}
var o = FindByTitle(title);
if(o == null)
{
New.Init(title, content).Save();
}
else
{
o.Content = content;
o.Save();
}
}
然后,我们运行单元测试,失败了,异常信息是:Lephone.Data.DbEntry的类型初始值设定项引发异常。恩,确实,我们的单元测试还没有配置数据源呢。
在VisualWiki.UnitTests中加入一个xml文件VisualWiki.config.xml,这个配置文件的配置项,基本上和VisualWiki中的Web.config一样,只是我这一次希望,其生成的SQL显示在单元测试的输出面板,而单元测试的输出面板直接接收Console的输出,另外,我希望Sqlite的数据库生成在系统的Temp目录下,修改后的VisualWiki.config.xml如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="Lephone.Settings" type="Lephone.Util.Setting.NameValueSectionHandler, Lephone.Util" />
</configSections>
<Lephone.Settings>
<add key="SqlLogRecorder" value="Lephone.Util.Logging.ConsoleMessageRecorder, Lephone.Util" />
<add key="NameMapper" value="Lephone.Util.Text.InflectionNameMapper, Lephone.Util" />
<!-- database defination -->
<add key="AutoCreateTable" value="true" />
<add key="DataBase" value="@SQLite : @{TempDirectory}UnitTest.db"/>
<add key="DbProviderFactory" value="System.Data.SQLite.SQLiteFactory, System.Data.SQLite, Version=1.0.60.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139"/>
</Lephone.Settings>
</configuration>
<configSections>
<section name="Lephone.Settings" type="Lephone.Util.Setting.NameValueSectionHandler, Lephone.Util" />
</configSections>
<Lephone.Settings>
<add key="SqlLogRecorder" value="Lephone.Util.Logging.ConsoleMessageRecorder, Lephone.Util" />
<add key="NameMapper" value="Lephone.Util.Text.InflectionNameMapper, Lephone.Util" />
<!-- database defination -->
<add key="AutoCreateTable" value="true" />
<add key="DataBase" value="@SQLite : @{TempDirectory}UnitTest.db"/>
<add key="DbProviderFactory" value="System.Data.SQLite.SQLiteFactory, System.Data.SQLite, Version=1.0.60.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139"/>
</Lephone.Settings>
</configuration>
然后将VisualWiki.config.xml的编译方式设置为内嵌的资源,另外,我们也希望单元测试完成时删除数据库文件,所以修改TestArticle加入处理函数:
private static readonly string TestFilePath = SystemHelper.TempDirectory + "UnitTest.db";
[TearDown]
public void TearDown()
{
File.Delete(TestFilePath);
}
[TearDown]
public void TearDown()
{
File.Delete(TestFilePath);
}
好,现在,运行单元测试,显示成功,点选单元测试项,我们还能在它的面板里看到在这个测试运行时,所生成的所有SQL语句。其实,使用Sqlite,还可以使用内存数据库来做单元测试,不过,内存数据库是连接型数据库,连接结束后,数据也就抛弃了,所以,需要我们让这个测试函数内的所有调用,都在一个连接里完成,在DbEntry里,可以用DbEntry.Context.UsingConnection来做,使用方法和UsingTransation类似。我以前以为内存数据库的速度更快,不过考虑后,我觉得,如果用于测试的数据库需要预先有N个表,每个表里还有一些测试数据的话,使用文件数据库,直接复制的方式,速度反而可能更快,DbEntry 500多个测试,有一半左右需要准备数据库,使用文件型数据库,所有单元测试运行完,只需要10秒左右。
现在,我们已经实现了SaveArticle函数,所以我们开始编写Edit.aspx页面,除了保存Wiki内容之外,我们还希望保存之后,直接返回Show.aspx页面,所以,修改好的Edit.aspx如下:
<%@ Page Title="" Language="C#" MasterPageFile="~/Main.master" %>
<script runat="server">
[HttpParameter] public string title;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
var article = Article.FindByTitle(title);
if (article != null)
{
Editor.Value = article.Content;
}
}
}
protected void Editor_SaveClick(object sender, EventArgs e)
{
Article.SaveArticle(title, Editor.Value);
Response.Redirect(new UrlBuilder("Show.aspx").Add("title", title).ToString());
}
</script>
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<fck:FCKeditor ID="Editor" runat="server" CssClass="editor" BasePath="~/fckeditor/"
Height="100%" onsaveclick="Editor_SaveClick">
</fck:FCKeditor>
</asp:Content>
<script runat="server">
[HttpParameter] public string title;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
var article = Article.FindByTitle(title);
if (article != null)
{
Editor.Value = article.Content;
}
}
}
protected void Editor_SaveClick(object sender, EventArgs e)
{
Article.SaveArticle(title, Editor.Value);
Response.Redirect(new UrlBuilder("Show.aspx").Add("title", title).ToString());
}
</script>
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<fck:FCKeditor ID="Editor" runat="server" CssClass="editor" BasePath="~/fckeditor/"
Height="100%" onsaveclick="Editor_SaveClick">
</fck:FCKeditor>
</asp:Content>
然后我们在VisualWiki的目录下建立fckeditor目录,把FckEditor的相关文件复制到这个目录下,现在,从Edit.aspx运行程序,显示一个异常:The Parameter title can't be empty。恩,在url后加上参数?title=test,然后回车,显示出FckEditor的编辑界面,只是高度有点儿小,随便输入一些内容,点击FckEditor上的保存按钮,页面返回Show.aspx,并且显示了刚才我们编辑的内容。把url中的Show修改为Edit,然后回车,我们又进入了编辑界面,这一次,编辑框里已经有了我们刚才编辑的内容,修改一下,再点击保存按钮,我们回到了Show页面,它也显示出了我们编辑后的内容。
现在,我们来编辑一下Main.master,用以提供一个Edit链接。母板页也需要title参数,所以先设置它从Lephone.Web.SmartMasterPageBase继承,以便使用HttpParameter,然后添加一个Edit的HyperLink控件,在Page_Load里对这个链接控件进行一些设置,修改后的Main.master如下:
<%@ Master Language="C#" Inherits="Lephone.Web.SmartMasterPageBase" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
[HttpParameter] public string title;
protected void Page_Load(object sender, EventArgs e)
{
var pageName = Request.Url.Segments[2].ToLower();
if(pageName == "edit.aspx")
{
Edit.Enabled = false;
}
else
{
Edit.Enabled = true;
Edit.NavigateUrl = new UrlBuilder("Edit.aspx").Add("title", title).ToString();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<asp:ContentPlaceHolder id="head" runat="server">
</asp:ContentPlaceHolder>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:HyperLink ID="Edit" runat="server">Edit</asp:HyperLink>
<br /><hr />
<asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server">
</asp:ContentPlaceHolder>
</div>
</form>
</body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
[HttpParameter] public string title;
protected void Page_Load(object sender, EventArgs e)
{
var pageName = Request.Url.Segments[2].ToLower();
if(pageName == "edit.aspx")
{
Edit.Enabled = false;
}
else
{
Edit.Enabled = true;
Edit.NavigateUrl = new UrlBuilder("Edit.aspx").Add("title", title).ToString();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<asp:ContentPlaceHolder id="head" runat="server">
</asp:ContentPlaceHolder>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:HyperLink ID="Edit" runat="server">Edit</asp:HyperLink>
<br /><hr />
<asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server">
</asp:ContentPlaceHolder>
</div>
</form>
</body>
</html>
再次从Show.aspx运行程序,添加参数?title=test,页面显示文章test,点击顶部的Edit链接,进入编辑页面,编辑之后,点击保存,回到显示页。
至此,VisualWiki项目已经可以进行可视化的新建、编辑操作,我们还需要进行很多的工作以便让它完成所有需求。不过现在,把它提交到SVN库里先。目前的源代码可以在这里下载:VisualWiki2.7z
未完待续……