Spiga

Asp.net MVC2 使用经验,性能优化建议

2010-03-22 15:51 by Otis's Technology Space, 4636 visits, 收藏, 编辑

这个月一直在用 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里。代码如下:

 public static class MvcHtmlHelper
    {
         public static MvcHtmlString MemcacheHtmlPartial(this HtmlHelper htmlHelper,int duration, string partialViewName, object model, ViewDataDictionary viewData)
        {
            object obaear = htmlHelper.ViewContext.RouteData.DataTokens["area"];
              string area=string.Empty;
              if (obaear != null) area = obaear.ToString();
            string key = string.Format("MemcacheHtmlPartial_{0}{1}", area, partialViewName);
            object ob = DistCache.Get(key);
            if (ob == null)
            {
                MvcHtmlString mstr = htmlHelper.Partial(partialViewName, model, viewData);
                DistCache.Add(key, mstr.ToString(), TimeSpan.FromSeconds(duration));
                return mstr;
            }
            else
            {
                 return MvcHtmlString.Create((string)ob);
            }
        }
}

 

然后,我觉得,这样,在每次请求时,还是要在Controller 里把数据取出来,然后再传到 Partial View里。 既然已经缓存了,就应该不用每次请求都要在Controller里把数据取出来才对!虽然数据层会有缓存。

所以我,能不能再省下去Controller取数据的消耗,于是又有了以下代码,其功能是:缓存Action生成的HTML到Memcached里。

  public static MvcHtmlString MemcacheHtmlRenderAction(this HtmlHelper htmlHelper, int duration, string actionName,string controllerName, RouteValueDictionary routeValues)
          {
              object obaear = htmlHelper.ViewContext.RouteData.DataTokens["area"];
              string area = string.Empty;
              if (obaear != null) area = obaear.ToString();
              string key = string.Format("MemcacheHtmlRenderAction_{0}{1}{2}", area, controllerName,actionName);
              object ob = DistCache.Get(key);
              if (ob == null)
              {
                  
                 // htmlHelper.RenderAction(actionName, controllerName, routeValues);
                  StringWriter writer = new StringWriter(CultureInfo.CurrentCulture);
                  ActionHelper(htmlHelper, actionName, controllerName, routeValues, writer);
                  string wStr = writer.ToString();
                  DistCache.Add(key, wStr,TimeSpan.FromSeconds(duration));
                  MvcHtmlString mstr = MvcHtmlString.Create(wStr);
                  
                  return mstr;
              }
              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方式了, 速度最快的,才是适合我的。呵。 而不是强类型的才是适合我的。

     

    最后,分享一个很实用的Asp.net MVC 分页Helper.

    这个Helper引自重典老大的blog:http://www.cnblogs.com/chsword/ . 我在之前做了少少修改,现已经在http://www.86e0.com/t 上使用了。

    效果如下:

    image

    请大家注意生成的 URL, 是用 ?参数=页码 的方式。代码如下:

     /// <summary>
            /// 分页Pager显示
            /// </summary>
            /// <param name="html"></param>
            /// <param name="currentPageStr">标识当前页码的QueryStringKey</param>
            /// <param name="pageSize">每页显示</param>
            /// <param name="totalCount">总数据量</param>
            /// <returns></returns>
            public static string Pager(this HtmlHelper html, string currentPageStr, int pageSize, int totalCount)
            {
                var queryString = html.ViewContext.HttpContext.Request.QueryString;
                int currentPage = 1; //当前页
                if(!int.TryParse(queryString[currentPageStr], out currentPage)) currentPage = 1; //与相应的QueryString绑定
                var totalPages = Math.Max((totalCount + pageSize - 1) / pageSize, 1); //总页数
                var dict = new RouteValueDictionary(html.ViewContext.RouteData.Values);
    
                var output = new StringBuilder();
    
                foreach (string key in queryString.Keys)
                    if (queryString[key] != null && !string.IsNullOrEmpty(key))
                        dict[key] = queryString[key];
                if (totalPages > 1)
                {
                    if (currentPage != 1)
                    {//处理首页连接
                        dict[currentPageStr] = 1;
                        output.AppendFormat("<span class=\"p_home\">{0}</span>", html.RouteLink("首页", dict));
                    }
                    if (currentPage > 1)
                    {//处理上一页的连接
                        dict[currentPageStr] = currentPage - 1;
                        output.AppendFormat("<span class=\"p_up\">{0}</span>", html.RouteLink("上一页", dict));
                    }
                    else
                    {
                        output.AppendFormat("<span class=\"p_disable\">{0}</span>","上一页");
                    }
                    int currint = 5;
                    for (int i = 0; i <= 10; i++)
                    {//一共最多显示10个页码,前面5个,后面5个
                        if ((currentPage + i - currint) >= 1 && (currentPage + i - currint) <= totalPages)
                            if (currint == i)
                            {//当前页处理
                                output.Append(string.Format("<span class=\"p_current\">{0}</span>", currentPage));
                            }
                            else
                            {//一般页处理
                                dict[currentPageStr] = currentPage + i - currint;
                                output.AppendFormat("<span class=\"p_num\">{0}</span>",html.RouteLink((currentPage + i - currint).ToString(), dict));
                            }
                    }
                    if (currentPage < totalPages)
                    {//处理下一页的链接
                        dict[currentPageStr] = currentPage + 1;
                        output.AppendFormat("<span class=\"p_down\">{0}</span>", html.RouteLink("下一页", dict));
                    }
                    else
                    {
                        output.AppendFormat("<span class=\"p_disable\">{0}</span>", "下一页");
                    }
                    if (currentPage != totalPages)
                    {
                        dict[currentPageStr] = totalPages;
                        output.AppendFormat("<span class=\"p_last\">{0}</span>",html.RouteLink("末页", dict));
                    }
                }
                output.AppendFormat("<span class=\"p_count\">第{0}页/共{1}页</span>", currentPage, totalPages);//这个统计加不加都行
                return output.ToString();
            }

     

     

    另: http://www.86e0.com/t 是做淘宝客类应用的。园子里还有谁在做淘宝客类网站么? 有的话多交流。^_^

     

  • Add your comment

    11 条回复

    1. #1楼 rad      2010-03-22 18:13
      http://www.tbmdw.com
      一样是淘宝客,一样是mvc,不过是1,懒得搞了,哈哈
       回复 引用 查看   
    2. #2楼[楼主] Otis's Technology Space      2010-03-22 18:21
      @rad
      ^_^, 原来园子里还真有朋友在搞这个。
       回复 引用 查看   
    3. #3楼 Jeffrey Zhao      2010-03-22 20:29
      没错,根据自己情况选择合适的做法才是最妥当的。
       回复 引用 查看   
    4. #4楼 隨風.NET      2010-03-22 20:44
      mark一下 等vs2010再换mvc2
       回复 引用 查看   
    5. #5楼[楼主] Otis's Technology Space      2010-03-22 21:05
      @Jeffrey Zhao
      ^_^ 老赵来了~!!
       回复 引用 查看   
    6. #6楼[楼主] Otis's Technology Space      2010-03-22 21:06
      @隨風.NET
      2010的RC已经非常不错了。 我直接把2008删除了。^_^
       回复 引用 查看   
    7. #7楼 leeolevis      2010-03-23 08:33
      不错 MARK
       回复 引用 查看   
    8. #8楼 [兴华]      2010-03-23 17:16
      类似网站一天能有多少PV?
      每月能从alimama获得多少盈利?
       回复 引用 查看   
    9. #9楼 Net205 Blog      2010-04-08 00:32
      淘宝客,早一个多月才知道。感觉现在做得多了,也不好搞吧
       回复 引用 查看   
    10. #10楼 TT.Net      2010-04-20 12:51
      ActionHelper 这个方法在MVC2里有吗?
       回复 引用 查看   
    11. #11楼[楼主] Otis's Technology Space      2010-04-21 10:22
      @TT.Net
      没喔。 我在mvc2源码里找出来的。
       回复 引用 查看