ASP.NET MVC3 小技巧:局部视图和缓存

上一篇文章中,我创建了一个Blog项目,今天我打算继续折腾一下这个项目。

如果你还不了解ASP.NET MVC3的布局,你可以先看看这篇文章,应该会对你有所帮助的。

主要讲一下自己对ASP.NET MVC3中的局部视图以及缓存的认识,不多说先上几幅图吧。

image

image image

第一幅图是博客首页分页栏的截图,第二幅图就是博客的Sidebar了。我分别用 ASP.NET MVC3 提供的两种局部视图实现。 有人可能会问,为什么要用两种不同的方法,你这不是装13吗? 。。。。我想我只能“呵呵”以对了,使用什么方法当然是根据需求而选的。

第三幅图是完成后的页面结构

先说分页栏吧

要做成上面的导航栏,我们需要哪些数据呢? 1. 当前页码  2. 每页显示多少文章 3. 数据库里有多少文章

OK,既然只要需要这些,那我们就在HomeController的IndexAction里通过ViewBag提供给他吧。

public class HomeController : Controller
{
    BlogDB db = new BlogDB();

    // GET: /Home/page/5
    public ActionResult Index(int page = 1)
    {
        int countPerPage=10;
        var posts = db.Posts.Include("Category")
                        .Include("Tags").Include("Comments")
                        .OrderByDescending(p=>p.CreateTime)
                        .Skip((page - 1) * countPerPage)
                        .Take(10);

        ViewBag.CurrentPage = page;
        ViewBag.CountPerPage = countPerPage;
        ViewBag.PostsCount = posts.Count();
        return View(posts);
    }
}

数据有了,就让我们开始创建局部视图吧。

打开右击View文件夹中的Share文件夹,选择添加视图。我这里将他命名为_Pagination,注意

image

小技巧 : 在 ASP.NET MVC3 中,如果一个视图以"_"开头命名的话,外部是不能单独访问他的,他只能作为局部视图嵌套在其他视图中。

打开刚刚创建好的视图,添加如下代码

@{
    int pageCount = ViewBag.PostsCount / ViewBag.CountPerPage + 1;
    int currentPage = ViewBag.CurrentPage;
}
<div class="pagination">
    <span class="first_page"><a href="/">First</a></span>
     @for (var i = 1; i <= pageCount; i++)
     {
         if (i == currentPage)
         {
            <span class="page_num current_page">
                <a href="/page/@i">@i</a>
            </span>
         }
         else
         { 
            <span class="page_num ">
                <a href="/page/@i">@i</a>
            </span>
         }
     }
    @if (currentPage == pageCount)
    {
        <span class="next_page"><a href="/page/@pageCount">Next</a></span>
    }
    else
    {
        <span class="next_page"><a href="/page/@(currentPage+1)">Next</a></span>
    }
</div>

我们在Home Index View中调用这个局部视图

@model IEnumerable<Blog.Models.Post>

@{
    ViewBag.Title = "NinoFocus | Notepad";
}

<div id="main">
@Html.Partial("_PostList", Model)

<!-- 看这里,看这里,我们通过下面的语句调用他 -->
@Html.Partial("_Pagination")
</div>
<!--#END main-->

恩,好了,分页栏就算完成了。

接下来Sidebar

Sidebar 和 分页栏 不同,因为分页栏只在首页显示,所有首页可以提供数据给他。而Sidebar确实无论哪个页面都要显示的,所以他必须要自己给自己提供数据。

好,看到这里可能有人会想,那还不和上面的一样吗?我在页面上写一些服务器端的代码,提供数据会死啊?

就像这样

<li>
    <h4 class="h4">
        <span>一些酷酷的 <strong>网站</strong></span></h4>
    <ul>
        @foreach (var link in new Blog.Models.BlogDB().FriendLinks)
        {
            <li>
		<a href="@link.URL" title="@link.Name">
		    <strong>@link.Name</strong>
		</a>
		 - @link.Description
	    </li>
        }
    </ul>
</li>

会死吗?当然不会啦,这也是一种解决方法啊(虽然我不推荐在)。但是,我想想又觉得不妥啊,Sidebar的数据更新频率很低的啊,这样每次刷新页面或者浏览其他页面都要重新请求数据库,这是不是不妥啊?

为了让他变得妥妥的,同时也充分的压榨一下ASP.NET MVC的特性。我还是决定使用ChildAction了。

image

添加一个ChildActionController,主要负责一些局部视图的工作(其实就是提供数据用的)。下面我以Sidebar中的友情链接为例,说一下Action的代码。

[ChildActionOnly]
[OutputCache(Duration = 3600)]
public PartialViewResult SidebarFriendLinks()
{
    var links = db.FriendLinks;
    return PartialView(links);
}

[ChildActionOnly] 属性说明这个Action只能内部调用,不能被外部单独访问。

[OutputCache] 启用缓存,Duration字段是缓存时间,以秒为单位,这里我设为1小时。

为了您的健康Action的返回值类型还是用 PartialViewResult 不要用平时常用的 ActionResult ,不然会死很多脑细胞的,我昨晚就被这个搞死。 ASP.NET MVC 遵循DRY(Don't repeat yourself)原则,如果你没有为ActionResult的View指定模板页的话,他会自动的给你加上默认的模板页。默认的模板页可以在_ViewStart.cshtml文件中指出

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

如果你看了我上面推荐的关于ASP.NET MVC3布局的文章,你会知道这是好东西的。

让我们模拟一下页面的调用,我们在使用了模板页的页面中调用SidebarFriendlinks,然后SidebarFriendLinks有调用了模板,然后模板页调用了SidebarFriendLinks,然后。。。。然后你就悲剧了啊。

如果您一定要使用ActionResult作为返回值的话,那就给局部页面加上下面的语句吧

@{
    Layout = null;
}

所以为了健康,还是使用PartialViewResult吧。

恩,就此打住,让我们继续吧。右击SidebarFriendLinks选择添加视图。

image

image

让我们打开SidebarFriendLinks.cshtml文件,修改一下@model 后的语句,然后添加页面代码

@model IEnumerable<Blog.Models.FriendLink>
<li>
    <h4 class="h4">
        <span>一些酷酷的 <strong>网站</strong></span></h4>
    <ul>
        @foreach (var link in Model)
        {
            <li>
            <a href="@link.URL" title="@link.Name">
                <strong>@link.Name</strong> 
            </a>
            - @link.Description
            </li>
        }
    </ul>
</li>

然后在_Sidebar.cshtml中调用他

<div class="sidebar">
    <ul>
        @Html.Action("SidebarTags","ChildAction")
        @Html.Action("SidebarArchive","ChildAction")
        @Html.Action("SidebarFriendLinks","ChildAction")  
    </ul>
</div>
<div class="clear">
</div>
<!-- END sidebar -->

这样写的 sidebar 以后我是不是就可以通过配置文件来控制里面的内容了呢,嘎嘎噶!!!

最后奉上_Layout.cshtml的代码,只求大家对这个博客的页面结构有个更加清晰的了解

<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    <link href="Content/Site.css" rel="stylesheet" type="text/css" />
</head>
<body class="homepage">
    <div id="container">
      @Html.Partial("_Header")
        @Html.Partial("_Navigation")
        <div id="body">
            <div id="content">
                @RenderBody()
            </div>
           @Html.Partial("_Sidebar") 
        </div>
    </div>
    @Html.Partial("_Footer")
    <!-- Script -->
    @RenderSection("footerScript",false) 
    <!-- End Script-->
</body>
</html>

@RenderBody() 就是其他页面填充内容的地方。

@RenderSection("footerScript",false) 预留的空间,第一个参数表示名称,第二个参数如果是false的话,表示不是每个页面都必须往里面添加内容;如果是true的话,就表示使用了这个模板的页面都必须往里面填写内容。

在页面上,可以这样调用

@section footerScript{ 
    ...javascript here
}

看看缓存是否有效

打开SQL Server Profiler来监视一下数据库请求。

1. 未开启缓存时

在没有开启缓存的情况下,我每次刷新博客首页,会有5条(我sidebar里放了3个局部页面)数据库请求

image

为什么使用EF时同样的SQL语句会被请求两次?求解!!!   我这里暂且算他一次先。

2. 开启缓存时

现在我启用sidebar的缓存,来看看刷新页面的数据库请求次数

image

请求次数从5次,变成2次了。说明缓存成功开启了。呵呵,有点爽爽的。

于是贪得无厌的我又把首页也加了缓存

//
// GET: /Home/page/5
[OutputCache(Duration=60*5)]
public ActionResult Index(int page = 1)

现在刷新页面看看

image

没有数据库连接请求了,啦啦啦啦。

貌似博客园的首页也加了缓存吧,好像一两分钟的样子。

标签:

posted on 2011-04-12 14:12 nizhuguo 阅读(...) 评论(...) 编辑 收藏

导航

统计