[转载]Asp.net MVC2 使用经验,性能优化建议 – Otis’s Technology Space – 博客园.
这个月一直在用 ASP.NET MVC2 做http://www.86e0.com/t 这个网站,用的时候是 aps.net MVC2 RC2,然后现在ASP.NET MVC2正式版已经是发布了。 在MVC的使用上,有一些心得。下面作一下总结,希望对大家有用,也欢迎大家讨论。
1.关于 缓存
缓存上,数据层上的缓存是必须的,这点不必多说了。
另一个很重要的是:视图片段缓存。
我参考了老赵的写的三篇关于片段缓存的文章:
本想用老赵的了,但是我发现Asp.net MVC2 的有一个新功能: Html.Partial可以返回生成的HTML, 返回的类型是:MvcHtmlString. 虽然要利用Partial View才能生成Html片段,但是我想这个已经够我用的了, 所以我做了一个这样一个Helper,主要是将生成的HTML片段缓存到Memcached里。代码如下:
01 |
public static class MvcHtmlHelper |
03 |
public static MvcHtmlString MemcacheHtmlPartial( this HtmlHelper htmlHelper, int duration, string partialViewName, object model, ViewDataDictionary viewData) |
05 |
object obaear = htmlHelper.ViewContext.RouteData.DataTokens[ "area" ]; |
06 |
string area= string .Empty; |
07 |
if (obaear != null ) area = obaear.ToString(); |
08 |
string key = string .Format( "MemcacheHtmlPartial_{0}{1}" , area, partialViewName); |
09 |
object ob = DistCache.Get(key); |
12 |
MvcHtmlString mstr = htmlHelper.Partial(partialViewName, model, viewData); |
13 |
DistCache.Add(key, mstr.ToString(), TimeSpan.FromSeconds(duration)); |
18 |
return MvcHtmlString.Create(( string )ob); |
然后,我觉得,这样,在每次请求时,还是要在Controller 里把数据取出来,然后再传到 Partial View里。 既然已经缓存了,就应该不用每次请求都要在Controller里把数据取出来才对!虽然数据层会有缓存。
所以我,能不能再省下去Controller取数据的消耗,于是又有了以下代码,其功能是:缓存Action生成的HTML到Memcached 里。
01 |
public static MvcHtmlString MemcacheHtmlRenderAction( this HtmlHelper htmlHelper, int duration, string actionName, string controllerName, RouteValueDictionary routeValues) |
03 |
object obaear = htmlHelper.ViewContext.RouteData.DataTokens[ "area" ]; |
04 |
string area = string .Empty; |
05 |
if (obaear != null ) area = obaear.ToString(); |
06 |
string key = string .Format( "MemcacheHtmlRenderAction_{0}{1}{2}" , area, controllerName,actionName); |
07 |
object ob = DistCache.Get(key); |
12 |
StringWriter writer = new StringWriter(CultureInfo.CurrentCulture); |
13 |
ActionHelper(htmlHelper, actionName, controllerName, routeValues, writer); |
14 |
string wStr = writer.ToString(); |
15 |
DistCache.Add(key, wStr,TimeSpan.FromSeconds(duration)); |
16 |
MvcHtmlString mstr = MvcHtmlString.Create(wStr); |
20 |
else { return MvcHtmlString.Create(( string )ob); } |
说明一下,Actionhelper的方法是在MVC原代码里提取出来的。 因为MVC2里的 Html.RenderAction方法并没有返回 MvcHtmlString的重载版。那位有更好的方法?
其实,MVC里的Action有输出缓存,所以直接在View里用 Html.RenderAction都可以解决很多问题了。这个主要是可以用程序管理缓存。
2.关于静态内容的放置
习惯上,静态内容会放在 mvc程序所在的目录下,比如说js,css,上传的图片等。但是这样的话,所有的静态请求都要经过 aspnet_isapi 处理,这样是非常不合算的。所以静态内容一般都会放在另外的子域上。http://www.86e0.com/t 是放在 cdn.86e0.com上。
3.关于强类型ViewModel
我基本上看了老赵的ASP.NET MVC最佳实践。 其中有一点,就是强烈推荐使用强类型的ViewModel. 我试了一些页面,发现用强类型的ViewModel,现阶段并不适用于我。因为我是用NbearLite,从数据库抓出来的大多是DataTable. 我是觉得DataTable+NbearLite蛮方便的,虽然没有动态语言的数据访问来得方便,但是比用Entity,ViewModel, DTO,等等来说,还是可以省下很多代码。然后,最重要的是,由于我这种站经常会修改,所以数据库改变,加字段,减字段是很经常性的事。但是,用 NbearLite + DataSet,DataTable,却非常方便。
所以我觉得,做Asp.net MVC,如果你不是用DDD,DDT的话,用DataTable还是可以的。因为DDD,DDT学习起来还是要点成本的。
4.关于URL生成
URL生成, 老赵写了一系列文章:
各 种URL生成方式的性能对比
各 种URL生成方式的性能对比(结论及分析)
为 URL生成设计流畅接口(Fluent Interface)
URL生成方式性能优化结果我直接选择
Raw方式了, 速度最快的,才是适合我的。呵。 而不是强类型的才是适合我的。
这个Helper引自重典老大的blog:http://www.cnblogs.com/chsword/ . 我在之前做了少少修改,现已经在http://www.86e0.com/t 上使用了。
效果如下:
请大家注意生成的 URL, 是用 ?参数=页码 的方式。代码如下:
04 |
/// <param name="html"></param> |
05 |
/// <param name="currentPageStr">标识当前页码的QueryStringKey</param> |
06 |
/// <param name="pageSize">每页显示</param> |
07 |
/// <param name="totalCount">总数据量</param> |
08 |
/// <returns></returns> |
09 |
public static string Pager( this HtmlHelper html, string currentPageStr, int pageSize, int totalCount) |
11 |
var queryString = html.ViewContext.HttpContext.Request.QueryString; |
13 |
if (! int .TryParse(queryString[currentPageStr], out currentPage)) currentPage = 1; |
14 |
var totalPages = Math.Max((totalCount + pageSize - 1) / pageSize, 1); |
15 |
var dict = new RouteValueDictionary(html.ViewContext.RouteData.Values); |
17 |
var output = new StringBuilder(); |
19 |
foreach ( string key in queryString.Keys) |
20 |
if (queryString[key] != null && ! string .IsNullOrEmpty(key)) |
21 |
dict[key] = queryString[key]; |
26 |
dict[currentPageStr] = 1; |
27 |
output.AppendFormat( "<span class=\"p_home\">{0}</span>" , html.RouteLink( " 首页" , dict)); |
31 |
dict[currentPageStr] = currentPage - 1; |
32 |
output.AppendFormat( "<span class=\"p_up\">{0}</span>" , html.RouteLink( " 上一页" , dict)); |
36 |
output.AppendFormat( "<span class=\"p_disable\">{0}</span>" , "上一页" ); |
39 |
for ( int i = 0; i <= 10; i++) |
41 |
if ((currentPage + i - currint) >= 1 && (currentPage + i - currint) <= totalPages) |
44 |
output.Append( string .Format( "<span class=\"p_current\">{0}</span>" , currentPage)); |
48 |
dict[currentPageStr] = currentPage + i - currint; |
49 |
output.AppendFormat( "<span class=\"p_num\">{0}</span>" ,html.RouteLink((currentPage + i - currint).ToString(), dict)); |
52 |
if (currentPage < totalPages) |
54 |
dict[currentPageStr] = currentPage + 1; |
55 |
output.AppendFormat( "<span class=\"p_down\">{0}</span>" , html.RouteLink( " 下一页" , dict)); |
59 |
output.AppendFormat( "<span class=\"p_disable\">{0}</span>" , "下一页" ); |
61 |
if (currentPage != totalPages) |
63 |
dict[currentPageStr] = totalPages; |
64 |
output.AppendFormat( "<span class=\"p_last\">{0}</span>" ,html.RouteLink( " 末页" , dict)); |
67 |
output.AppendFormat( "<span class=\"p_count\">第{0}页/共{1}页</span>" , currentPage, totalPages); |
68 |
return output.ToString(); |
另: http://www.86e0.com/t 是做淘宝客类应用的。园子里还有谁在做淘宝客类网站么? 有的话多交流。^_^