好记性不如烂键盘

每一个小问题都是深究的引子
posts - 18, comments - 176, trackbacks - 0, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

2011年4月11日

在之前的文章MVC3缓存之二:页面缓存中的局部动态中,没有注意到MVC3的版本中对输出缓存进行了修改,园友的评论中提及了此问题,所以又去抽时间看了下局部缓存的解决方案。

最后发现在发布的MVC3版本中,新增了一个叫做Partial Page的东西,即可以对载入到当前页面的另外的一个View进行缓存后输出,这与我们之前讨论的局部动态刚好相反了,即之前我们进行这个页面的缓存,然后对局部进行动态输出,现在的解决方案是:页面时动态输出的,而对需要缓存的局部进行缓存处理。查来查去还没有看到局部动态的解决方案,所以我们先看看局部缓存的处理方法。

局部缓存(Partial Page)

我们先建立一个需要局部缓存的页面View,叫做PartialCache.cshtml,页面内容如下:

<p>@ViewBag.Time2</p>

在其对应的Controller中添加对应的Action

[OutputCache(Duration = 10)]
public ActionResult PartialCache()
{
    ViewBag.Time2 
= DateTime.Now.ToLongTimeString();
    
return PartialView();
}

我们可以看到对其Action做了缓存处理,对页面进行缓存10秒钟。

而在Index的View中调用此缓存了的页面则需要这样:

@{
    ViewBag.Title = "Index";
}
<h2>
    OutputCache Demo
</h2>
<p>
    No Cache
</p>
<div>@DateTime.Now.ToLongTimeString()
</div>
<br />
<p>
    Partial Cache 10 mins
</p>
<div class="bar2">@Html.Action("PartialCache", "Index", null)</div>

运行后,我们刷新页面可以发现Index的主体没有缓存,而引用到的PartialCache进行了10秒缓存的处理。


下载源码

posted @ 2011-04-11 14:59 Parry 阅读(2707) 评论(5) 编辑

2011年3月29日

上一篇我们讨论了MVC中使用页面缓存的一些方法,而其中由于页面缓存的粒度太粗,不能对页面进行局部的缓存,或者说,如果我们想在页面缓存的同时对局部进行动态输出该怎么办?下面我们看下这类问题的处理。

MVC中有一个Post-cache substitution的东西,可以对缓存的内容进行替换。 

使用Post-Cache Substitution

  • 定义一个返回需要显示的动态内容string的方法。
  • 调用HttpResponse.WriteSubstitution()方法即可。

示例,我们在Model层中定义一个随机返回新闻的方法。

using System;
using System.Collections.Generic;
using System.Web;

namespace MvcApplication1.Models
{
    
public class News
    {
        
public static string RenderNews(HttpContext context)
        {
            var news 
= new List<string> 
                { 
                    
"Gas prices go up!"
                    
"Life discovered on Mars!"
                    
"Moon disappears!" 
                };
            
            var rnd 
= new Random();
            
return news[rnd.Next(news.Count)];
        }
    }

}  

然后在页面中需要动态显示内容的地方调用。

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcApplication1.Views.Home.Index" %>
<%@ Import Namespace="MvcApplication1.Models" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    
<title>Index</title>
</head>
<body>
    
<div>  
    
<% Response.WriteSubstitution(News.RenderNews); %>        
    
<hr />    
    The content of this page is output cached.
    
<%= DateTime.Now %>    
    
</div>
</body>

</html> 

如在上一篇文章中说明的那样,给Controller加上缓存属性。

using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
    [HandleError]
    
public class HomeController : Controller
    {
        [OutputCache(Duration
=60, VaryByParam="none")]
        
public ActionResult Index()
        {
            
return View();
        }
    }

}

可以发现,程序对整个页面进行了缓存60s的处理,但调用WriteSubstitution方法的地方还是进行了随机动态显示内容。

对Post-Cache Substitution的封装

将静态显示广告Banner的方法封装在AdHelper中。

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;

namespace MvcApplication1.Helpers
{
    
public static class AdHelper
    {
        
public static void RenderBanner(this HtmlHelper helper)
        {
            var context 
= helper.ViewContext.HttpContext;
            context.Response.WriteSubstitution(RenderBannerInternal);
        }
        
        
private static string RenderBannerInternal(HttpContext context)
        {
            var ads 
= new List<string> 
                { 
                    
"/ads/banner1.gif"
                    
"/ads/banner2.gif"
                    
"/ads/banner3.gif" 
                };

            var rnd 
= new Random();
            var ad 
= ads[rnd.Next(ads.Count)];
            
return String.Format("<img src='{0}' />", ad);
        }
    }

这样在页面中只要进行这样的调用,记得需要在头部导入命名空间。

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcApplication1.Views.Home.Index" %>
<%@ Import Namespace="MvcApplication1.Models" %>
<%@ Import Namespace="MvcApplication1.Helpers" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    
<title>Index</title>
</head>
<body>
    
<div>
    
<% Response.WriteSubstitution(News.RenderNews); %>    
    
<hr />    
    
<% Html.RenderBanner(); %>    
    
<hr />    
    The content of this page is output cached.
    
<%= DateTime.Now %>
    
</div>
</body>

</html>  

使用这样的方法可以使得内部逻辑对外呈现出更好的封装。

posted @ 2011-03-29 11:08 Parry 阅读(3185) 评论(10) 编辑

2011年3月19日

在以前的WebForm的开发中,在页面的头部加上OutputCache即可启用页面缓存,而在MVC3中,使用了Razor模板引擎的话,该如何使用页面缓存呢?

如何启用

在MVC3中要如果要启用页面缓存,在页面对应的Action前面加上一个OutputCache属性即可。

我们建一个Demo来测试一下,在此Demo中,在View的Home目录下的Index.cshtml中让页面输入当前的时间。

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    
<title>Index</title>
</head>
<body>
    
<div>
        
<h2>
            现在时间:@DateTime.Now.ToString("T")
</h2>
    
</div>
</body>
</html>

在Controllers中添加对应的Action,并加上OutputCache属性。

[HandleError]
public class HomeController : Controller
{
    [OutputCache(Duration 
= 5, VaryByParam = "none")]
    
public ActionResult Index()
    {
        
return View();
    }
}

刷新页面即可看到页面做了一个10秒的缓存。当页面中数据不是需要实时的呈现给用户时,这样的页面缓存可以减小实时地对数据处理和请求,当然这是针对整个页面做的缓存,缓存的粒度还是比较粗的。

缓存的位置

可以通过设置缓存的Location属性,决定将缓存放置在何处。

Location可以设置的属性如下:

· Any
· Client
· Downstream
· Server
· None
· ServerAndClient

Location的默认值为Any。一般推荐将用户侧的信息存储在Client端,一些公用的信息存储在Server端。

加上Location应该像这样。

[HandleError]
public class HomeController : Controller
{
    [OutputCache(Duration 
= 5, VaryByParam = "none", Location = OutputCacheLocation.Client, NoStore = true)]
    
public ActionResult Index()
    {
        
return View();
    }
} 

缓存依赖

VaryByParam可以对缓存设置缓存依赖条件,如一个产品详细页面,可能就是根据产品ID进行缓存页面。

缓存依赖应该设置成下面这样。

在MVC3中对输出缓存进行了改进,OutputCache不需要手动指定VaryByParam,会自动使用Action的参数作为缓存过期条件。(感谢”散客游“提醒)

[HandleError]
public class HomeController : Controller
{
    [OutputCache(Duration 
= int.MaxValue, VaryByParam = "id")]
    
public ActionResult Index()
    {
        
return View();
    }
}

另一种通用的设置方法

当我们需要对多个Action进行统一的设置时,可以在web.config文件中统一配置后进行应用即可。

在web.config中配置下Caching节点

<caching>
<outputCacheSettings>
    
<outputCacheProfiles>
        
<add name="Cache1Hour" duration="3600" varyByParam="none"/>
    
</outputCacheProfiles>
</outputCacheSettings>

</caching>

那么在Action上使用该配置节点即可,这样的方法对于统一管理配置信息比较方便。

[HandleError]
public class HomeController : Controller
{
    [OutputCache(CacheProfile 
= "Cache1Hour")]
    
public ActionResult Index()
    {
        
return View();
    }
}

posted @ 2011-03-19 11:15 Parry 阅读(4105) 评论(9) 编辑

2011年1月28日

在前端优化的各种金律铁规中,“减少客户端对资源的请求”都会在其中出现,刚好最近对网站做一些优化,使用了一下Combres组件,有点心得,遂整理成文。

园子中也有几篇Combres组件的介绍,如:Combres库学习小结以及部分源码分析使用Combres 库 ASP.NET 网站优化。可部署时参考起来显得有些简略,所以此文也算对此类文章的补充。

配置组件

此组件的一些作用和原理在我上面提及的两篇文章中有介绍,可以自行移步至对应的文章中查看。这里还有有作者介绍的一些详细的使用方法

下载Combres组件,下载下来的包里包含了DLL、帮助文件、源码和一些例子,我们现在直接来看如何部署。

在下载下来的\Binary\merged\中有一个Combres.dll,在readme文件中得知其对可能要用到的dll都进行了打包,如Combres.dll、fasterflect.dll、log4net.dll、ajaxmin.dll、 yahoo.yui.compressor.dll等等。

在项目中引用此dll,下面来配置下配置文件。

首先配置下web.config。

在configSections配置节下添加

1 <section name="combres" type="Combres.ConfigSectionSetting, Combres, Version=2.0.0.0, Culture=neutral, PublicKeyToken=49212d24adfbe4b4"/> 

在configuration配置节下添加Combres配置文件的路径,此文件的作用我们下面再说。

1 <combres definitionUrl="~/App_Data/combres.xml"/>

配置好了后应该像这样

1 <configSections>
2       <section name="combres" type="Combres.ConfigSectionSetting, Combres, 
3     Version=2.0.0.0, Culture=neutral, PublicKeyToken=49212d24adfbe4b4"/>
4  </configSections>
5  <combres definitionUrl="~/App_Data/combres.xml"/>

还需要添加httpModules的节点

1 <httpModules>
2         <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, 
3     System.Web.Routing, Version=3.5.0.0, Culture=neutral, 
4     PublicKeyToken=31BF3856AD364E35"/>
5 </httpModules>

注意:需要在项目中添加对System.Web.Routing的引用。

下面来配置Combres的配置文件,按照上面的路径配置,我们就在App_Data下添加combres.xml文件。

添加如下格式的配置文件。 

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <combres xmlns='urn:combres'>
 3   <resourceSets url="~/combres.axd" defaultDuration="30" 
 4                                 defaultVersion="auto" 
 5                                 defaultDebugEnabled="auto" >
 6     <resourceSet name="siteCss" type="css">
 7       <resource path="~/styles/site.css" />
 8       <resource path="~/styles/jquery-ui-1.7.2.custom.css" />
 9     </resourceSet>
10     <resourceSet name="siteJs" type="js">
11       <resource path="~/scripts/jquery-1.3.2.js" />
12       <resource path="~/scripts/jquery-ui-1.7.2.custom.min.js" />
13     </resourceSet>
14   </resourceSets>
15 </combres>    
  • defaultDuration 默认缓存的时间,单位为天数
  • defaultVersion 合并后的资源版本,在你修改了资源文件后需要对版本进行修改,你可以指定auto或者手动设置一个版本号
  • defaultDebugEnabled 调试的模式,为true时那么资源文件不进行压缩,开发时可以设置成true,上线后设置成false

 具体添加压缩方法的配置节点,用于选择哪种方法对资源文件进行压缩

 1 <cssMinifiers>
 2     <minifier name="yui" type="Combres.Minifiers.YuiCssMinifier, Combres">
 3       <param name="CssCompressionType" type="string" value="StockYuiCompressor" />
 4       <param name="ColumnWidth" type="int" value="-1" />
 5     </minifier>
 6   </cssMinifiers>
 7   <jsMinifiers>
 8     <minifier name="msajax" type="Combres.Minifiers.MSAjaxJSMinifier, Combres" 
 9     binderType="Combres.Binders.SimpleObjectBinder, Combres">
10       <param name="CollapseToLiteral" type="bool" value="true" />
11       <param name="EvalsAreSafe" type="bool" value="true" />
12       <param name="MacSafariQuirks" type="bool" value="true" />
13       <param name="CatchAsLocal" type="bool" value="true" />
14       <param name="LocalRenaming" type="string" value="CrunchAll" />
15       <param name="OutputMode" type="string" value="SingleLine" />
16       <param name="RemoveUnneededCode" type="bool" value="true" />
17       <param name="StripDebugStatements" type="bool" value="true" />
18     </minifier>
19   </jsMinifiers>

 要使用哪种压缩方法,在resourceSet或者在resource上添加相应的属性即可,配置后像下面这样

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <combres xmlns='urn:combres'>
 3   <cssMinifiers>
 4     <minifier name="yui" type="Combres.Minifiers.YuiCssMinifier, Combres">
 5       <param name="CssCompressionType" type="string" value="StockYuiCompressor" />
 6       <param name="ColumnWidth" type="int" value="-1" />
 7     </minifier>
 8   </cssMinifiers>
 9   <jsMinifiers>
10     <minifier name="msajax" type="Combres.Minifiers.MSAjaxJSMinifier, Combres" 
11     binderType="Combres.Binders.SimpleObjectBinder, Combres">
12       <param name="CollapseToLiteral" type="bool" value="true" />
13       <param name="EvalsAreSafe" type="bool" value="true" />
14       <param name="MacSafariQuirks" type="bool" value="true" />
15       <param name="CatchAsLocal" type="bool" value="true" />
16       <param name="LocalRenaming" type="string" value="CrunchAll" />
17       <param name="OutputMode" type="string" value="SingleLine" />
18       <param name="RemoveUnneededCode" type="bool" value="true" />
19       <param name="StripDebugStatements" type="bool" value="true" />
20     </minifier>
21   </jsMinifiers>
22   <resourceSets url="~/combres.axd" defaultDuration="30" 
23                                 defaultVersion="auto" 
24                                 defaultDebugEnabled="auto" >
25     <resourceSet name="siteCss" type="css" minifierRef="yui">
26       <resource path="~/styles/site.css" />
27       <resource path="~/styles/jquery-ui-1.7.2.custom.css" />
28     </resourceSet>
29     <resourceSet name="siteJs" type="js">
30       <resource path="~/scripts/jquery-1.3.2.js" minifierRef="msajax"  />
31       <resource path="~/scripts/jquery-ui-1.7.2.custom.min.js" minifierRef="off" />
32     </resourceSet>
33   </resourceSets>
34 </combres>

最后还需要在Global.ascx的Application_Start中添加加载的方法即可

1 protected void Application_Start(object sender, EventArgs e)
2 {
3     RouteTable.Routes.AddCombresRoute("Combres Route");
4 }

 记得在Global.ascx的头部要引入命名空间

1 <%@ Import Namespace="System.Web.Routing" %>
2 <%@ Import Namespace="Combres" %>

页面使用

1 <%@ Import Namespace="Combres" %>
2 <head runat="server">
3     <title>Using Combres</title>
4     <%= WebExtensions.CombresLink("siteCss"%>
5     <%= WebExtensions.CombresLink("siteJs"%>
6 </head>

页面使用效果

 

当页面中引用了很多个js、css文件时,将都被合并成两个请求,以达到减少HTTP请求的目的。  

posted @ 2011-01-28 16:44 Parry 阅读(2890) 评论(14) 编辑

2011年1月24日

在前端优化中,js、css等文件的优化一般都是压缩的优化,进行合并、减小体积以达到减小请求的目的。

今天发现了一个集成在VS中的压缩插件,使得压缩变得比较快捷。

配置方法

首先需要去下载Microsoft Ajax Minifier,一路安装就可以,如果VS正在使用,需要重启。

 在你的web项目上右键,点击卸载项目,使得变成灰色。

在卸载的项目上点击编辑,在proj文件的添加配置节点,像下面这样

配置节点说明。参考了此博文中的一段:

Include属性是需要进行压缩的目录路径并带文件通配符

Exclude则是不需要进行压缩的文件路径

WebResources是我根目录下的文件夹,可以根据实际项目进行相应的调整

AjaxMin节点是压缩的一些参数设置,具体可以参考下面的连接进行更多的设置

对项目进行编译,对应的文件夹中就出现了重命名且压缩后的资源文件了,发布时直接使用即可。

注意

当遇到我们的web程序不是webapplication而是website形式时,需要安装Web Deployment,这个是For VS2010的,其他版本在页面的底部可以找到。

安装后在添加的web_deploy项目上进行上述的操作即可。

生成的资源文件在Web_deploy\Debug\对应的文件夹中。

压缩效果

压缩比还是很明显的,推荐使用。

posted @ 2011-01-24 14:37 Parry 阅读(331) 评论(1) 编辑

2010年11月18日

摘要: 无意在网上看到了一份.NET下实现Server Push(服务器推)的源码,断断续续看了两天,难点较多,遂成此文。关于服务器推技术,我们最深刻的体验应该是WebQQ的使用,通过服务器向客户端进行信息推送,而不是客户端去主动取数据(Client Pull)。在AJAX技术大行其道之时,服务器推技术才是未来的主角。AJAX 只是实现了单用户的响应回调,与服务器的异步通讯,但是并不能反映多用户的协同相应...阅读全文

posted @ 2010-11-18 14:30 Parry 阅读(1043) 评论(7) 编辑

2010年11月17日

摘要: 在上篇跨域SSO的实现之一:架构设计中主要谈及了跨域SSO的实现原理,在这篇中主要介绍下此功能的实现。此文章还是对应于第一篇翻译文章的第二部分:Single Sign On (SSO) for cross-domain ASP.NET applications: Part-II - The implementation。今天读了一遍,发现都是对第一篇文章中原理的再解释。并且作者还提供了一个SSOL...阅读全文

posted @ 2010-11-17 11:15 Parry 阅读(598) 评论(1) 编辑

2010年10月28日

摘要: 翻译自CodeProject网站ASP.NET9月份最佳文章:Single Sign On (SSO) for cross-domain ASP.NET applications。翻译不妥之处还望大家多多指导、相互交流。文章分为两部分:架构设计和程序实现,此为第一篇即:架构设计或者叫设计蓝图(Part-I - The design blue print)。:) 简介周一的早晨,当你正在纳闷周末咋就...阅读全文

posted @ 2010-10-28 11:34 Parry 阅读(4729) 评论(34) 编辑

2010年10月26日

摘要: HTTP压缩HTTP压缩是在Web服务器和浏览器间传输压缩文本内容的方法。HTTP压缩采用通用的压缩算法如GZIP等压缩HTML、JavaScript或CSS文件。压缩的最大好处就是降低了网络传输的数据量,从而提高客户端浏览器的访问速度。当然,同时也会增加一点点服务器的负担。GZIP是比较常见的一种HTTP压缩算法。压缩测试在部署的IIS 6.0上配置了GZIP压缩,详细的测试结果如下。未开启GZ...阅读全文

posted @ 2010-10-26 09:20 Parry 阅读(3598) 评论(29) 编辑

摘要: 开发的CMS系统中模板引擎和标签的开发算是告一段落,而在Discuz的CMS系统SupeSite中借鉴到了很多东西,略作小记,好记性永远不如烂键盘。当需要将页面解析成静态页面时,当然需要借助模板引擎去对标签作解析。而标签数据源的定义颇值得研究。在先前的标签设计中,由于数据存储使用动态模型,所以并没有涉及到多张表的逐个定义标签解析。而在后期的开发中,脱离动态模型的表越来越多,而每一个表的数据解析,就...阅读全文

posted @ 2010-10-26 09:15 Parry 阅读(203) 评论(0) 编辑