DbEntry 开发实践:Wiki 系统(三)
书接上文,我们有了Show.aspx和Edit.aspx页面,现在应该开发历史记录功能了。
不过,在做这个之前,我们先加入一个Default.aspx,用来转向到有参数的Show.aspx页面,省得每次要自己输入title参数:
现在,我们来考虑历史记录问题。一般来说,我们只工作在最新的Wiki页之上,所以,我们需要另一个表来保存历史数据,就叫ArticleHistory吧,它和Article应该是一对多关系,隶属于Article,而且,它的Init方法应该接受一个Article作为参数,所以,代码如下:
再次运行单元测试,显示成功,业务逻辑正确。现在我们需要一个页面来显示历史,这个历史页面,同样接受一个title参数,而且使用GridView来显示其创建时间,并给出一个链接,用以阅读历史页面,Show页面就很好,只要多接受一个版本参数就可以了。说到GridView,我们首先需要使用DbEntryDataSource,先在VisualWiki.Models里建立一个DataSources目录,然后创建ArticleHistoryDataSource:
然后在VisualWiki项目下创建History.aspx页面,修改其代码如下:
运行程序,编辑Home页面几次,然后,将url里的Show修改为History,回车,就看到了我们的编辑历史了。当然,现在点击GridView里的Show链接,显示的仍然是最新的Wiki页面,所以,我们来修改一下Show.aspx,增加HistoryID参数:
现在,重新运行程序,确认Show页面已经可以按照HistoryID正确的显示历史数据。恩,总是修改url也不是办法,我们还是修改一下Main.master,加入Histroy链接吧:
咳咳,我们在写重复的代码了,看来需要小小的重构一下,不过在那之前,先把修改提交到SVN吧。目前的源代码可以在这里下载:VisualWiki3.7z
未完待续……
不过,在做这个之前,我们先加入一个Default.aspx,用来转向到有参数的Show.aspx页面,省得每次要自己输入title参数:
代码
<%@ Page Title="" Language="C#" MasterPageFile="~/Main.master" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
Response.Redirect(new UrlBuilder("Show.aspx").Add("title", "Home").ToString());
}
</script>
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
</asp:Content>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
Response.Redirect(new UrlBuilder("Show.aspx").Add("title", "Home").ToString());
}
</script>
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
</asp:Content>
现在,我们来考虑历史记录问题。一般来说,我们只工作在最新的Wiki页之上,所以,我们需要另一个表来保存历史数据,就叫ArticleHistory吧,它和Article应该是一对多关系,隶属于Article,而且,它的Init方法应该接受一个Article作为参数,所以,代码如下:
代码
namespace VisualWiki.Models
{
public abstract class ArticleHistory : LinqObjectModel<ArticleHistory>
{
public abstract string Content { get; set; }
[Index(ASC = false)]
public abstract DateTime CreatedOn { get; set; }
[BelongsTo]
public abstract Article Article { get; set; }
public ArticleHistory Init(Article article)
{
Content = article.Content;
CreatedOn = article.SavedOn;
return this;
}
}
}
{
public abstract class ArticleHistory : LinqObjectModel<ArticleHistory>
{
public abstract string Content { get; set; }
[Index(ASC = false)]
public abstract DateTime CreatedOn { get; set; }
[BelongsTo]
public abstract Article Article { get; set; }
public ArticleHistory Init(Article article)
{
Content = article.Content;
CreatedOn = article.SavedOn;
return this;
}
}
}
[HasMany(OrderBy = "CreatedOn DESC, Id DESC")]
public abstract IList<ArticleHistory> History { get; set; }
public abstract IList<ArticleHistory> History { get; set; }
代码
[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);
Assert.AreEqual(0, o1.History.Count);
Article.SaveArticle("title", "hello, I'm coming");
var o2 = Article.FindByTitle("title");
Assert.IsNotNull(o2);
Assert.AreEqual("hello, I'm coming", o2.Content);
Assert.AreEqual(1, o2.History.Count);
Assert.AreEqual("whatever it is", o2.History[0].Content);
}
public void TestSaveArticle()
{
Article.SaveArticle("title", "whatever it is");
var o1 = Article.FindByTitle("title");
Assert.IsNotNull(o1);
Assert.AreEqual("whatever it is", o1.Content);
Assert.AreEqual(0, o1.History.Count);
Article.SaveArticle("title", "hello, I'm coming");
var o2 = Article.FindByTitle("title");
Assert.IsNotNull(o2);
Assert.AreEqual("hello, I'm coming", o2.Content);
Assert.AreEqual(1, o2.History.Count);
Assert.AreEqual("whatever it is", o2.History[0].Content);
}
代码
public static void SaveArticle(string title, string content)
{
var article = FindByTitle(title);
if(article == null)
{
article = New.Init(title, content);
}
else
{
article.History.Add(ArticleHistory.New.Init(article));
article.Content = content;
}
article.Save();
}
{
var article = FindByTitle(title);
if(article == null)
{
article = New.Init(title, content);
}
else
{
article.History.Add(ArticleHistory.New.Init(article));
article.Content = content;
}
article.Save();
}
再次运行单元测试,显示成功,业务逻辑正确。现在我们需要一个页面来显示历史,这个历史页面,同样接受一个title参数,而且使用GridView来显示其创建时间,并给出一个链接,用以阅读历史页面,Show页面就很好,只要多接受一个版本参数就可以了。说到GridView,我们首先需要使用DbEntryDataSource,先在VisualWiki.Models里建立一个DataSources目录,然后创建ArticleHistoryDataSource:
namespace VisualWiki.Models.DataSources
{
public class ArticleHistoryDataSource : DbEntryDataSource<ArticleHistory>
{
}
}
{
public class ArticleHistoryDataSource : DbEntryDataSource<ArticleHistory>
{
}
}
然后在VisualWiki项目下创建History.aspx页面,修改其代码如下:
代码
<%@ Page Title="" Language="C#" MasterPageFile="~/Main.master" %>
<script runat="server">
[HttpParameter] public string title;
protected void Page_Load(object sender, EventArgs e)
{
var article = Article.FindByTitle(title);
if(article != null)
{
ArticleHistoryDataSource1.AddAndCondition(p => p.Article.Id == article.Id);
}
var hlf = (HyperLinkField) HistoryList.Columns[HistoryList.Columns.Count - 1];
hlf.DataNavigateUrlFormatString = new UrlBuilder("~/Show.aspx").Add("title", title) + "&HistoryID={0}";
}
</script>
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<asp:GridView ID="HistoryList" runat="server" AllowPaging="True"
AutoGenerateColumns="False" BackColor="White" BorderColor="#E7E7FF"
BorderStyle="None" BorderWidth="1px" CellPadding="3" DataKeyNames="Id"
DataSourceID="ArticleHistoryDataSource1" GridLines="Horizontal"
EmptyDataText="Can't find any items.">
<RowStyle BackColor="#E7E7FF" ForeColor="#4A3C8C" />
<Columns>
<asp:BoundField DataField="CreatedOn" HeaderText="Created On"
SortExpression="CreatedOn" />
<asp:HyperLinkField Text="Show" DataNavigateUrlFields="Id"
DataNavigateUrlFormatString="~/Show.aspx?HistoryID={0}" >
</asp:HyperLinkField>
</Columns>
<FooterStyle BackColor="#B5C7DE" ForeColor="#4A3C8C" />
<PagerStyle BackColor="#E7E7FF" ForeColor="#4A3C8C" HorizontalAlign="Right" />
<SelectedRowStyle BackColor="#738A9C" Font-Bold="True" ForeColor="#F7F7F7" />
<HeaderStyle BackColor="#4A3C8C" Font-Bold="True" ForeColor="#F7F7F7" />
<AlternatingRowStyle BackColor="#F7F7F7" />
</asp:GridView>
<biz:ArticleHistoryDataSource ID="ArticleHistoryDataSource1" runat="server" DefaultOrderBy="CreatedOn DESC, Id DESC" />
</asp:Content>
<script runat="server">
[HttpParameter] public string title;
protected void Page_Load(object sender, EventArgs e)
{
var article = Article.FindByTitle(title);
if(article != null)
{
ArticleHistoryDataSource1.AddAndCondition(p => p.Article.Id == article.Id);
}
var hlf = (HyperLinkField) HistoryList.Columns[HistoryList.Columns.Count - 1];
hlf.DataNavigateUrlFormatString = new UrlBuilder("~/Show.aspx").Add("title", title) + "&HistoryID={0}";
}
</script>
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<asp:GridView ID="HistoryList" runat="server" AllowPaging="True"
AutoGenerateColumns="False" BackColor="White" BorderColor="#E7E7FF"
BorderStyle="None" BorderWidth="1px" CellPadding="3" DataKeyNames="Id"
DataSourceID="ArticleHistoryDataSource1" GridLines="Horizontal"
EmptyDataText="Can't find any items.">
<RowStyle BackColor="#E7E7FF" ForeColor="#4A3C8C" />
<Columns>
<asp:BoundField DataField="CreatedOn" HeaderText="Created On"
SortExpression="CreatedOn" />
<asp:HyperLinkField Text="Show" DataNavigateUrlFields="Id"
DataNavigateUrlFormatString="~/Show.aspx?HistoryID={0}" >
</asp:HyperLinkField>
</Columns>
<FooterStyle BackColor="#B5C7DE" ForeColor="#4A3C8C" />
<PagerStyle BackColor="#E7E7FF" ForeColor="#4A3C8C" HorizontalAlign="Right" />
<SelectedRowStyle BackColor="#738A9C" Font-Bold="True" ForeColor="#F7F7F7" />
<HeaderStyle BackColor="#4A3C8C" Font-Bold="True" ForeColor="#F7F7F7" />
<AlternatingRowStyle BackColor="#F7F7F7" />
</asp:GridView>
<biz:ArticleHistoryDataSource ID="ArticleHistoryDataSource1" runat="server" DefaultOrderBy="CreatedOn DESC, Id DESC" />
</asp:Content>
运行程序,编辑Home页面几次,然后,将url里的Show修改为History,回车,就看到了我们的编辑历史了。当然,现在点击GridView里的Show链接,显示的仍然是最新的Wiki页面,所以,我们来修改一下Show.aspx,增加HistoryID参数:
代码
<%@ Page Title="" Language="C#" MasterPageFile="~/Main.master" %>
<script runat="server">
[HttpParameter(AllowEmpty = true)] public long? HistoryID;
[HttpParameter] public string title;
protected void Page_Load(object sender, EventArgs e)
{
if (HistoryID == null)
{
var article = Article.FindByTitle(title);
ContextHolder.Text
= article != null
? article.Content
: "The wiki page does not exist. The page will be created the first time you edit it. ";
}
else
{
var h = ArticleHistory.FindById(HistoryID.Value);
if(h != null)
{
ContextHolder.Text = h.Content;
}
}
}
</script>
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<asp:Label ID="ContextHolder" runat="server" Text=""></asp:Label>
</asp:Content>
<script runat="server">
[HttpParameter(AllowEmpty = true)] public long? HistoryID;
[HttpParameter] public string title;
protected void Page_Load(object sender, EventArgs e)
{
if (HistoryID == null)
{
var article = Article.FindByTitle(title);
ContextHolder.Text
= article != null
? article.Content
: "The wiki page does not exist. The page will be created the first time you edit it. ";
}
else
{
var h = ArticleHistory.FindById(HistoryID.Value);
if(h != null)
{
ContextHolder.Text = h.Content;
}
}
}
</script>
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<asp:Label ID="ContextHolder" runat="server" Text=""></asp:Label>
</asp:Content>
现在,重新运行程序,确认Show页面已经可以按照HistoryID正确的显示历史数据。恩,总是修改url也不是办法,我们还是修改一下Main.master,加入Histroy链接吧:
代码
<%@ 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();
}
if(pageName == "history.aspx")
{
History.Enabled = false;
}
else
{
History.Enabled = true;
History.NavigateUrl = new UrlBuilder("History.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> |
<asp:HyperLink ID="History" runat="server">History</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();
}
if(pageName == "history.aspx")
{
History.Enabled = false;
}
else
{
History.Enabled = true;
History.NavigateUrl = new UrlBuilder("History.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> |
<asp:HyperLink ID="History" runat="server">History</asp:HyperLink>
<br /><hr />
<asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server">
</asp:ContentPlaceHolder>
</div>
</form>
</body>
</html>
咳咳,我们在写重复的代码了,看来需要小小的重构一下,不过在那之前,先把修改提交到SVN吧。目前的源代码可以在这里下载:VisualWiki3.7z
未完待续……