继续mvc4的研究学习,今天先来研究缓存技术。因为每次都去读取数据库,是效率极其低下的,尤其是对一些变化不是很大,实时性要求不是很高的内容,比如分类列表,地市列表之类的,实在不应该每次都进行磁盘操作。虽然数据库也有缓存策略,但是我们更应该将他挡在web层,在一次访问后,应当一定时期内从缓存读取数据,而根本不要建立起数据库的连接。
当然可以自己写缓存,不过mvc究竟有没有已经实现的缓存方案呢。
查阅资料,发现有OutputCacheAttribute这个继承自ActionFilterAttribute的Attribute,可以轻松实现缓存action结果。
[OutputCache(Duration = 30)]
只需在action或者controller上加上此特性,并指定缓存时间,就可以实现缓存页面输出。当然也可以用在缓存json输出上。注意Duration的单位为秒。
接下来要考虑些实际问题了。对于静态页,缓存当然不会有问题,或者一些只读的Action 也是没问题的,但是 如果action中有处理Form提交数据,并且有数据库操作,这个缓存显然是不能做的。可是偏偏有一些页面,一部分是可以、需要缓存的,展示的部分,另一部分 确是要处理的,不能缓存的部分(常见于同一个action时,用HttpPostAttribute修饰的处理Action)。这个怎么做呢。
考虑两种解决方案:
1是将需要缓存的部分,做成partialVIew和相应的action,在主体View中引用。
2是提交不允许再次的数据绑定以绕开需要缓存的部分,具体做法可为
a、提交通过ajax方式,返回后处理,ajax到action时,action不缓存。
b、不用ajax提交,仍旧是表单提交,只是提交后不进行页面绑定直接跳转(如果绑定了,缓存策略等于白做了,虽然get方式打开时 使用了缓存,但是一旦提交数据,又要访问数据库),此action一样不能缓存。 这就涉及到了一会儿要讲的,如何在添加、修改、删除完成后进行页面弹出提示和跳转。
当然,根据参数不同而做出不同缓存策略,想也是支持的。看看Attibute其他的参数:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class OutputCacheAttribute : ActionFilterAttribute, IExceptionFilter
{
// 摘要:
// 初始化 System.Web.Mvc.OutputCacheAttribute 类的新实例。
public OutputCacheAttribute();
// 摘要:
// 获取或设置缓存配置文件名称。
//
// 返回结果:
// 缓存配置文件名称。
public string CacheProfile { get; set; }
//
// 摘要:
// 获取或设置子操作缓存。
//
// 返回结果:
// 子操作缓存。
public static System.Runtime.Caching.ObjectCache ChildActionCache { get; set; }
//
// 摘要:
// 获取或设置缓存持续时间(以秒为单位)。
//
// 返回结果:
// 缓存持续时间。
public int Duration { get; set; }
//
// 摘要:
// 获取或设置位置。
//
// 返回结果:
// 位置。
public OutputCacheLocation Location { get; set; }
//
// 摘要:
// 获取或设置一个值,该值指示是否存储缓存。
//
// 返回结果:
// 如果应存储缓存,则为 true;否则为 false。
public bool NoStore { get; set; }
//
// 摘要:
// 获取或设置 SQL 依赖项。
//
// 返回结果:
// SQL 依赖项。
public string SqlDependency { get; set; }
//
// 摘要:
// 获取或设置基于内容变化的编码。
//
// 返回结果:
// 基于内容变化的编码。
public string VaryByContentEncoding { get; set; }
//
// 摘要:
// 获取或设置基于自定义项变化的值。
//
// 返回结果:
// 基于自定义项变化的值。
public string VaryByCustom { get; set; }
//
// 摘要:
// 获取或设置基于标头变化的值。
//
// 返回结果:
// 基于标头变化的值。
public string VaryByHeader { get; set; }
//
// 摘要:
// 获取或设置基于参数变化的值。
//
// 返回结果:
// 基于参数变化的值。
public string VaryByParam { get; set; }
}
其中有类似VaryByxxxx的一堆可疑属性,微软的注释实在是不知所云,去msdn上看到的跟元数据里的注释也没什么差别,看了还是跟没看一样。具体怎么使用,等有空在研究。(知道的朋友也可以交流下)
先看第二个问题,返回处理结果并弹出提示,然后跳转。这个在webform时代,使用ScriptManager可以很好的解决,RegisterStartupScripts等一系列函数轻松实现了跳转后或者ajax的绑定(区别仅仅在于有没有updatepanel)。 但是在mvc,大有回复html本质的赶脚(虽然还是面目全非了,但是相对于webform这种从代码直接看,完全看不出生成的html是啥样的来说,确实是贴近了许多)。 于是,弹出提示,看起来似乎麻烦了点。
最直接的想法,对于ajax,可以通过返回的json来判断操作结果,然后根据结果来直接alert相应提示,这个比较好理解。只是,这样,必须用ajax方式,提交到的action不能返回view了,如果返回partialview仅仅是一句alert和href,似乎有点大材小用。于是,我们来做个统一点的开关好了。
修改_Layout.cshtml,这个是在_ViewStart.cshtml里指定的模板view,_ViewStart.cshtml属于约定的,所有view初始化时都会先走一遍的view。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
@if (ViewBag.Alert != null && ViewBag.AlertRedirect != null)
{
<script type="text/javascript">
alert('@ViewBag.Alert');
window.location = '@ViewBag.AlertRedirect';
</script>
}
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
@RenderSection("css", required: false)
</head>
<body>
@if (ViewBag.Alert == null || ViewBag.AlertRedirect == null)
{
@RenderBody()
}
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/jqueryval")
@RenderSection("scripts", required: false)
@if (ViewBag.Alert != null && ViewBag.AlertRedirect == null)
{
<script type="text/javascript">
alert('@ViewBag.Alert');
</script>
}
</body>
</html>
什么意思呢,很简单了, 通过两个动态属性来判断返回的VIew是不是需要弹出提示和跳转。
ViewBag.Alert和ViewBag.AlertRedirect都是字符串。
如果定义了 ViewBag.Alert,并且定义了ViewBag.AlertRedirect。在页面绑定时,head里会出现弹出提示和跳转的js语句,并且,整个body部分将不再绑定,生成的页面弹出提示内容为ViewBag.Alert,点击确定后就会跳转到ViewBag.AlertRedirect的地址。
如果定义了ViewBag.Alert但是没有定义ViewBag.AlertRedirect,页面会正常绑定,并且在页面的最后生成alert语句。页面效果是在页面加载的最后一步弹出提示,这个时候 页面已经全部显示出来了。
如果ViewBag.Alert未定义,那么ViewBag.AlertRedirect不管有没有定义都不会产生影响,结果都是body正常绑定,头和尾都不会有alert出现。
这样,如果需要弹出消息,可以放心大胆的按照写普通action一样写,最后在return View();之前,只需要稍稍加一句
ViewBag.Alert="修改成功";
就实现了返回的view会弹出提示。是不是很神奇。