最近需要实现一个功能,当页面上的数据发生变化时把变化之前的页面生成一个快照并保存到数据库,这样就可以在任何时候看到当时的页面内容。

比较简单的解决方法我想到2种:

  1. 把页面上的数据都保存下来,然后需要展示历史页面的时候就用这些历史数据配上当前的页面视图显示出来。

    这种方法有一个显而易见的问题:如果页面视图也发生了变化怎么办?另外一个数据一个数据的保存也很麻烦。

  2. 将整个页面的 html 保存下来。展示的时候只需要把 html 读出来就行,不需要什么额外的操作。

    这种方式看起来更简单、可行,可以说是真正的快照。一个需要注意的问题就是页面 html 的大小,如果页面太大,并且更新很频繁,那么很快就会占用数据库的大量空间。因此在保存之前进行一下数据压缩会比较好。这种用时间换空间的做法特别适用于此,因为在我们这个系统中,这些历史页面不会被经常、密集地访问。

获取页面的内容,其实就是执行一个特定 Controller 的 Action 并获取它生成的 Html 代码。如果是在一个 View 里,可以很容易的通过 HtmlHelper 的 Action 方法得到。但是如果是在一个 Controller 里呢?可以在 Controller 里用 HtmlHelper 的 Action 方法吗?答案当时可以,只要你为 HtmlHelper 提供了必要的信息——满足它的构造函数的需要。

HtmlHelper 类的构造函数需要的信息,大部分我们当前 Controller 本身就可以提供,对于一些当前 Controller 无法提供的,我们就造一个出来:

1
HtmlHelper helper = new HtmlHelper(new ViewContext(ControllerContext, new FakeView(), ViewData, TempData, System.IO.TextWriter.Null), new ViewPage());

其中HtmlHelper类的构造函数需要2个参数:ViewContextIViewDataController,对于IViewDataController,我们直接借用一个 ViewPage,因为ViewPage实现了IViewDataController。而ViewContext,我们需要继续满足它的构造函数要求。ViewContext的构造函数需要的参数中,除了IViewTextWriter,其它都可以直接利用我们当前 Controller 的ControllerContext, ViewDataTempData属性。而TextWriter,如代码所示,直接传个TextWriter.Null了事。IView 呢,我们可以构造一个空 View,就是这里的FakeView

1
2
3
4
5
6
7
public class FakeView : IView
{
public void Render(ViewContext viewContext, TextWriter writer)
{
throw new InvalidOperationException();
}
}

通过以上我们的努力,已经有了一个在 Controller 里可用的 HtmlHelper,接下来就像在 View 里一样,调用它的 Action 方法来得到需要的 Html 吧。

留言

2014-06-28