November 2006 - Posts
Useful Logging Options in .Net:
http://blog.joycode.com/tingwang/articles/80349.aspx
持续更新中……这可能是我写过的所有东西中最有用的一样,就看识不识货了……如果有补充,请留言,谢谢……
微软公司昨天发布了一个Windows Communication Foundation (WCF)和Windows CardSpace的示例程序包,内容极为丰富,从最简单的Hello World到复杂的解决方案一应俱全。对于Windows Communication Foundation (WCF)和Windows CardSpace这两个新东西而言,这些示例程序无疑就是最好的学习资源。
下载地址:http://www.microsoft.com/downloads/details.aspx?FamilyID=0043041f-95d6-4eb7-966a-c21a737e193a&DisplayLang=en
这是部分目录截图:
顺便简单介绍一下这两个东西:
Windows Communication Foundation (WCF)(开发代号:Indigo)是微软公司在.NET 3.0下的SOA框架,它也在微软公司的规划中占有非常重要的位置。无论是在组织内部还是组织之间,应用程序通信都是现代软件的基本功能,.NET Framework 3.0 用WCF以SOA的方式提供了程序之间沟通的渠道。下面是官方网站上的介绍:
The Windows Communication Foundation (previously codenamed "Indigo") is Microsoft's unified framework for building secure, reliable, transacted, and interoperable distributed applications.
关于Windows Communication Foundation (WCF),还有如下资源也不错:
- Windows Communication Foundation官方网站: http://wcf.netfx3.com/
- Bruce Zhang的《WCF之旅》系列
- 这个PPT也不错,适合快速了解一下:http://wcf.netfx3.com/files/folders/6680/download.aspx
Windows CardSpace(开发代号:InfoCard)是微软公司新一代的统一数字身份标识控件,旨在解决现今网络上各种身份认证的复杂性、不安全性等问题。这是官方网站上的介绍:
Windows CardSpace enables users to provide their digital identities in a familiar, secure and easy way. In the physical world we use business cards, credit cards and membership cards. Online with CardSpace we use a variety of virtual cards to identify ourselves, each retrieving data from an identity provider. Don't struggle with usernames and passwords, just choose an information card!
这篇文章对Windows CardSpace有一些较为详细的介绍,尚不了解的朋友也可以借此入门。
Windows Vista简体中文版本已经可以在MSDN下载到了。

【原文地址】 Tip/Trick: Use the ASP.NET 2.0 CSS Control Adapters for CSS friendly HTML output
【原文发表日期】 Wednesday, November 29, 2006 11:01 PM
厌烦了内置的ASP.NET服务器端控件生成 HTML <table> 元素,而希望你能使用纯粹的CSS方案?如果是这样,读下去。。。
上个星期,我们发布了ASP.NET 2.0 CSS 控件适配器的1.0正式版。这些适配器利用了ASP.NET 2.0 中的一个我们称之为“控件适配架构”的新扩展性特性,允许开发人员覆盖(override),修改(modify),或细调(tweak )现有服务器控件的显示输出逻辑,而不必改动控件的属性,支持的事件或编程模型等。
上个星期我们发布的ASP.NET 2.0 CSS 控件适配器提供了11个最常用的ASP.NET 控件的预制控件适配器,这些控件包括GridView, DetailsView,FormsView, DataList, Menu, TreeView, Login, LoginStatus,CreateUserWizard,ChangePassword 和PasswordRecovery。CSS 控件适配器将使这些ASP.NET 控件发出CSS友好(friendly)的HTML输出,除去了象行内style和 <table> 元素这样的东西,在你的网站上使用CSS时这可是极其有用的。
点击下面任何一个连接看一下它们是如何改变这些内置ASP.NET控件生成的标识之前和之后的例子:
CSS 控件适配器工具包同时包括了所有上述控件适配器的VB和C#源代码。你可以不作任何修改而按原样使用这些源代码来得到纯粹的CSS输出。或者假如你要进一步细调输出的话,你可以进去修改这些适配器来生成你想要的任何自定义标识。
2个月前我曾贴过一篇很好的教程,演示了你怎么使用CSS 控件适配器,我强烈建议你阅读一下,学一下如何起步。
Scott Mitchell 今天早些时候在 www.4guysfromrolla.com 上也贴了一篇非常好的关于CSS 控件适配器的文章,对如何使用它们做了讨论。
你可以在 http://www.asp.net 的 CSS Control Adapters论坛 上对如何使用它们提问和得到帮助。
希望本文对你有所帮助,
Scott
附注:
1。我要向Russ和Heidi表示特别的感谢,感谢他们在建造CSS控件适配器和样本例子方面所作的出色工作!
2。想阅读我另外的ASP.NET 技巧/诀窍博客帖子的话,请浏览我的ASP.NET 技巧,诀窍和资源网页。
【原文地址】 Extending the ASP.NET 2.0 Localization Model with a Database Resource Provider
【原文发表日期】 Wednesday, November 29, 2006 10:04 PM
今春早些时候,
Michèle Leroux Bustamante 在MSDN上写了一篇题为《
ASP.NET 2.0本地化特性:本地化Web应用的一个新颖方法》的精彩文章。它讨论了如何使用ASP.NET 2.0中的新本地化特性来建造本地化的,culture-aware的Web应用的基本知识。
想观看如何尽快地运用这些本地化技巧,我也强烈建议你看一下这个13分钟的免费录像:《我该如何使用本地化来创建多语言网站?》。这个录像是非常精彩的《ASP.NET 2.0 How Do I 》录像系列中的一部,演示了如何按步就班地从头建造起和本地化一个ASP.NET应用,以及如何根据来自客户端的user-agent字符串来动态地选择语言,和让用户通过一个语言下拉框来直接选择语言。
在过去的几个月内大家常问的一个问题是哪里有更好的文档,描述了如何在内置于ASP.NET 2.0 和 VS2005中默认的基于XML的RESX 方案之外的资源提供器里存储本地化的资源字符串。上个月,Michelle在MSDN上发表了另一篇题为《扩展ASP.NET 2.0资源提供器模型》的精彩文章。这篇文章集中介绍了那些允许开发人员从另外的存储地方获取资源,以及与页面分析,编译,以及运行时执行集成的特性。该文还包含了演示如何使用数据库储存本地化资源内容的细节和样例代码。
Michelle在今后还会发表本地化系列中的另外2篇文章,这些文章将讨论如何进一步与VS 2005集成资源提供器,以及如何处理复杂的资源层次结构等。
希望本文对你有所帮助,
Scott
微软将在明天发布Office System 2007、Windows Vista、Exchange 2007,我会在发布会上做一个Office客户端开发的Session。虽然这不是我的(最)强项(嘿嘿,汗。。。)但我也准备了几个自认为还不错的demo例子:
1、Office 2007 Open XML文件格式浏览
2、Office2007文档的自动修复
3、不借助Office API生成Office文档
4、在线生成规定格式的Office文档
5、Word 2007 Ribbon开发
6、Word 2007 任务栏开发
会议结束后我会陆续贴出来~
另外,Dflying同志刚刚贴了这篇
http://blog.joycode.com/dflying/archive/2006/11/29/88303.aspx
非常棒的资源文章,大家一定要记得下载
随着Office System 2007的发布,微软公司的新一代企业业务平台变得前所未有的强大。Office相关开发也正逐渐变得炙手可热。为了帮助开发者更好地了解并基于Office System 2007进行开发,微软公司将发布一系列有关Office System 2007的参考文档以及相关辅助软件。
昨天微软公司就发布了如下三个与Office System 2007相关的软件和参考文档:
[1] 2007 Office System Document: Compliance Features in the 2007 Microsoft Office System
下载地址:http://www.microsoft.com/downloads/details.aspx?FamilyID=d64dfb49-aa29-4a4b-8f5a-32c922e850ca&DisplayLang=en
这篇洋洋洒洒的67页的文档全面介绍了Office System 2007的适应性以及扩展性能力。每一个应用了Office System 2007的企业都将会有它自己的个性化、需要定制的需求。这份文档就将告诉我们Office System 2007开发者什么样的需求是能够实现的,应该怎样实现等相关内容。文档份为如下几大部分:
- Introduction
- An Overview of Regulatory Compliance
- The 2007 Microsoft Office System Products
- Compliance Capabilities in the 2007 Microsoft Office System
- Compliance Extensibility Opportunities
- Development Tools for Extending Office and Windows SharePoint Services
- Summary
- Appendix I: Resources
- Appendix II: References
如果你打算定制出一套自己的Office System 2007系统,那么这份文档绝对不容错过。
[2] 2007 Office System Document: Lists of Control IDs
下载地址:http://www.microsoft.com/downloads/details.aspx?FamilyID=4329d9e9-4d11-46a5-898d-23e4f331e9ae&DisplayLang=en
Office System 2007的UI中引入了一个新东西——Ribbon。虽然对于这个Ribbon,使用者仁者见仁,众说纷纭,不过作为开发者,我们还是有必要赶上发展的脚步。Ribbon这个东西相关的开发也设计得独具匠心,具体内容就不详细说了,有兴趣的朋友可以先参考一下这篇MSDN文档:Customizing the Office (2007) Ribbon User Interface for Developers (Part 1 of 3) 。
微软公司发布的这个软件其实是一个自解压的压缩文件,解压后将得到24个Excel文件,其中分别列出了Office System 2007系列软件中使用的内建Ribbon的ID,方便我们开发时参考。
下图就显示了Word中内建的部分Ribbon的ID:
[3] 2007 Office System Sample: Open XML File Format Code Snippets for Visual Studio 2005
下载地址:http://www.microsoft.com/downloads/details.aspx?FamilyID=8d46c01f-e3f6-4069-869d-90b8b096b556&DisplayLang=en
不得不承认,随着Office System 2007的发布,Office开发变得更加简化,提供的API也更加丰富。不过由于Office System 2007本身的复杂性,对于初学者来说,掌握Office System 2007开发仍旧不是一件容易的事情。甚至对于一些最常用功能的实现都无所适从。
微软公司发布的这个Visual Studio 2005的Code Snippets集合就提供了一系列关于Office System 2007开发中经常用到的功能的代码片断。关于Visual Studio 2005的Code Snippets,其实就是一系列常用的代码片断,可以看作是一种代码级别的复用。这里不再多谈Code Snippets,如果你还不是很了解这个强大功能,请参考这篇MSDN文章:How to: Manage Code Snippets。
下面就是在Visual Studio 2005中使用该Code Snippets时的界面:
如上图所示,选择了“Excel: Get sheet info”之后,Code Snippets将自动插入如下一大段代码:
public struct SheetInfo
{ public string SheetName;
public string SheetType;
public SheetInfo(string SheetName, string SheetType)
{ this.SheetName = SheetName;
this.SheetType = SheetType;
}
}
public List<SheetInfo> XLGetSheetInfo(string fileName)
{ // Return a generic list containing info about all the sheets.
const string documentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
// Fill this collection with a list of all the sheets
List<SheetInfo> sheets = new List<SheetInfo>();
using (Package xlPackage = Package.Open(fileName, FileMode.Open, FileAccess.Read))
{ // Get the main document part (workbook.xml).
foreach (System.IO.Packaging.PackageRelationship relationship in xlPackage.GetRelationshipsByType(documentRelationshipType))
{ // There should only be one document part in the package.
Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), relationship.TargetUri); PackagePart documentPart = xlPackage.GetPart(documentUri);
// Load the contents of the workbook, which is all you
// need to retrieve the names and types of the sheets:
XmlDocument doc = new XmlDocument();
doc.Load(documentPart.GetStream());
// Create a NamespaceManager to handle the default namespace,
// and create a prefix for the default namespace:
XmlNamespaceManager nsManager = new XmlNamespaceManager(doc.NameTable);
nsManager.AddNamespace("default", doc.DocumentElement.NamespaceURI);
// Loop through all the nodes, retrieving the information
// about each sheet:
foreach (System.Xml.XmlNode node in doc.SelectNodes("//default:sheets/default:sheet", nsManager)) { string sheetName = string.Empty;
string sheetType = "worksheet";
sheetName = node.Attributes["name"].Value;
XmlAttribute typeAttr = node.Attributes["type"];
if (typeAttr != null)
{ sheetType = typeAttr.Value;
}
sheets.Add(new SheetInfo(sheetName, sheetType));
}
// There's only one document part.
break;
}
}
return sheets;
}
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
我们既可以直接使用这些已经生成好了的功能,也可以通过查看代码了解、学习Office System 2007的常用操作,简直爽呆了!
今天在服务器上外部发布新站点 http://groups.csdn.net/ 时候,在服务器上竟然找不到 Microsoft .NET Framework 2.0 Configuration 菜单项。
对比其他机子,发现 Microsoft .NET Framework 2.0 Configuration 菜单项指向的是类似如下的目录:
"C:\Program Files\Microsoft.NET\SDK\v2.0 64bit\Bin\ 而公司服务器上,C:\Program Files\Microsoft.NET 这个目录根本不存在。查进入后,发现公司服务器上装的是:
Microsoft .NET Framework 2.0 版可再发行组件包 (x86)
http://www.microsoft.com/downloads/details.aspx?displaylang=zh-cn&FamilyID=0856eacb-4362-4b0d-8edd-aab15c5e04f5
也就是24M的那个版本,这个版本不包含GAC操作的工具。
你如果需要有GAC操作的工具等工具,你可以继续装开发工具包。也就是如下版本:
Microsoft .NET Framework 2.0 软件开发工具包 (SDK) (x86)
http://www.microsoft.com/downloads/details.aspx?familyid=FE6F2099-B7B4-4F47-A244-C96D69C35DEC&displaylang=zh-cn
这个版本有425M,当然你安装的时候,你可以只选择装其中的工具,例子和帮助文档不装。
转眼间已经快两年没有管理这个blog了,想想真是对不起开心大哥!
旧地重游恍如隔世。
如果有人看过我现在个人网站的blog(http://www.wuyuntian.com/blogx)的内容就会发现我现在感兴趣的已经不是编程而是中国传统文化的学习。
虽然我的工作仍然还是软件开发,但我现在仅仅把编程当成工作,工作之余基本不再研究。
人都说:老骥伏枥,壮心不已!我是恰恰相反。
我想过一种:
白日看云坐,清秋对雨眠。眉头无一事,笔下有千年。
的生活。可能真是老了的原因吧,开始感叹世事无常了。
茶淡味悠远,书古意赜深,红尘无尽景,大千多沉沦。
如果对blog中的内容有兴趣,如果不怕失望可以通过我的邮箱:
nolan.shang@gmail.com
或者我的个人网站:http://www.wuyuntian.com
和我联系!
这就算是告别吧!开心大哥可将我的帐号和空间回收了。诚挚感谢!
何日人长久,一笑泯恩仇,聚散任凭缘,千里不用愁。
就在明天,就在中国大饭店,微软中国将正式面向企业客户发布2007 Microsoft Office system、Windows Vista和Exchange Server 2007。我将在下午的技术讲座中向大家奉献一节《让开发人员爱上 Microsoft Office SharePoint Server 2007 的7个理由》。
点击这里查看发布会详细议程。
非常感谢大家热情支持博客堂源代码开发计划,我们仍然在招募人选。虽然大家从项目组主页上看到其项目人员已经将近十人了,但由于大家日常工作比较繁忙,所以还是希望更多的志愿者加入。
另外,各位已经加入的朋友,请到该http://www.codeplex.com/joycode的Forum(Who are you)中介绍一下自己的基本情况,让所有团队成员能够相互认识,了解一下大家的基本情况,以便未来更好的分派工作。
同时,我们现在已经公布了一些新的功能集,您可以在“http://www.codeplex.com/WorkItem/List.aspx?ProjectName=joycode”中查看到(这个服务器对于中文支持不好,大家将就着看一下吧),您可以对您喜欢的功能进行投票,以便催促我们的团队成员尽快开发相应的功能,当然,您也可以在http://www.codeplex.com/Project/ListThreads.aspx?ProjectName=joycode&ForumId=2460中提交您遇到的Bug,或者希望得到的新功能,请注意:该论坛不支持中文,请务必使用英文。
Let's go!
在Thread pooling for web connections中我曾经提到Shared Source CLI,这次再借用另一个场合宣传一下这个"Open Source .NET Framework"吧。
Topic还是web connection pooling:可以很自然地想到我们可以设计这么一个class:
class WebConnector
{
WebRequest CreateRequest(string targetUrl)
{
return WebRequest.Create(targetUrl);
}
...
}
这个class现在什么都没有做,因为我们的希望是.NET提供的WebRequest可以提供一定的pooling mechanism,如果真是那样,那生活真是很美好,我们什么都不要做就可以下班了。而其实这也不是什么太wild的dream,毕竟是.NET,连个pooling都不提供,也不要混了。
在深入研究一下WebRequest class之前,我们还需要考虑一个问题:web application很常见的会用到其他的web service,也就是用SOAP像其他web service请求服务。在Visual Studio中如何生成web service的proxy class等等我就不用重复重复了吧(wsdl.exe等等的)?关键在于:这个由wsdl自动生成proxy class用的是什么connection mechanism呢?虽然基本可以想到应该也是somewhere, somehow用的WebRequest,但还是让我们double check一下。找任意一份wsdl生成的proxy class的代码看看:
public partial class WSProxcyClass : System.Web.Services.Protocols.SoapHttpClientProtocol
嗯,看来基本功能都在SoapHttpClientProtocol这个类里面。
再研究一下MSDN里SoapHttpClientProtocol的API,aha,有这么一个member function:
protected override WebRequest GetWebRequest(Uri uri)
看来所有SoapHttpClientProtocol里面用到的WebRequest都是通过这个函数来取得的。注意一下前缀的override关键字,很好很好,这说明如果我们对WebRequest现有的pooling mechanism不满意的话(或者WebRequest根本没有提供pooling机制),我们可以从SoapHttpClientProtocol派生一个类,然后改写掉GetWebRequest来实现我们满意的pooling机制。其他的接口都还是用SoapHttpClientProtocol,而WSProxyClass只要继承新的派生类就可以了。
嗯,很好,至少我们有了backup plan,虽然麻烦了一点点,但也算不错,感谢SoapHttpClientProtocol良好的界面。
现在该进一步了,我们来研究一下WebRequest到底有没有提供pooling吧。
(题外话:可以看到.NET源代码的朋友可以研究一下SoapHttpClientProtocol的实现,这里是一个class hierarchy:
WebClientProtocol
-> HttpWebClientProtocol
-> SoapHttpClientProtocol :
你会发现,GetWebRequest其实一直延伸到最基类的WebClientProtocol里,有不少messy stuff to deal with,所以真的要改写GetWebRequest并非那么容易。不过,anyway, that's a side note to this article, we are heading for better stuff, baby.
)
这下该用到,搜一下webrequest.cs,嗯嗯,time to read the source code:
private static WebRequest Create(Uri requestUri, bool useUriBase) {
string LookupUri;
WebRequestPrefixElement Current = null;
bool Found = false;
if (!useUriBase) {
LookupUri = requestUri.AbsoluteUri;
}
else {
// schemes are registered as <schemeName>":", so add the separator
// to the string returned from the Uri object
LookupUri = requestUri.Scheme + ':';
}
int LookupLength = LookupUri.Length;
// Copy the prefix list so that if it is updated it will
// not affect us on this thread.
ArrayList prefixList = PrefixList;
// Look for the longest matching prefix.
// Walk down the list of prefixes. The prefixes are kept longest
// first. When we find a prefix that is shorter or the same size
// as this Uri, we'll do a compare to see if they match. If they
// do we'll break out of the loop and call the creator.
for (int i = 0; i < prefixList.Count; i++) {
Current = (WebRequestPrefixElement)prefixList
;
// See if this prefix is short enough.
if (LookupLength >= Current.Prefix.Length) {
// It is. See if these match.
if (String.Compare(Current.Prefix, 0, LookupUri, 0, Current.Prefix.Length, StringComparison.OrdinalIgnoreCase ) == 0) {
// These match. Remember that we found it and break
// out.
Found = true;
break;
}
}
}
WebRequest webRequest = null;
if (Found) {
// We found a match, so just call the creator and return what it
// does.
webRequest = Current.Creator.Create(requestUri);
return webRequest;
}
// Otherwise no match, throw an exception.
throw new NotSupportedException(SR.GetString(SR.net_unknown_prefix));
}
WebRequest.Create就是最后的生成一个个实际的request object的地方。如果有pooling mechanism,在这里也该看出些端倪来了。
嗯,这段代码实在太有意思了。看懂了么?WebRequest有一个url prefixlist这样的列表,对于每个注册过的url prefix,都有一个对应的Creator来create相应的webrequest。可以想象的,一定还有一个" * "这样的prefix来匹配所有没有注册的prefix。
而匹配prefix的过程更是有趣,如果你仔细研究并理解我用红色highlight的comment的话,一定可以看出为什么了。不过这个不是我的focus,我就不分析了。
重要的是,我们知道有Creator这么一个东西,而且用户可以注册不同的url用不同的Creator,比如对于连接到www.microsoft.com的webrequest有一种要求,对于连接到www.amazon.com的webrequest有另一种要求,这些要求都可以customize。
^_^ 很好很好,我们已经很接近了。现在只有最后两个问题了,怎么customize webrequest? 以及,可以customize到什么程度?
嗯,第二个问题很好,我猜,答案是。没准,嗯,没准可以customize的程度很高?高到可以让我们指定pooling的功能!!
现在所有的线索都串起来了,我们知道有那么一个Creator,特别是那个匹配" * " (即所有)url的Creator,它应该可以是customize的,而且很可能允许我们指定pooling的一些参数。
那好,现在只有最后一个问题了:怎么customize?
难不成要我们自己派生几个Creator的子类,然后改写一下Create函数??
太复杂了吧。藏的这么深,谁知道这一把怎么玩的。。。
嗯,找找线索。
而线索也只有一条了:Creator的type!
Current = (WebRequestPrefixElement)prefixList
;
webRequest = Current.Creator.Create(requestUri);
Bingo! WebRequestPrefixElement!
Hmm, element, element, element... 想到了什么?web.config里的configuration element么!
web.config -- we are almost done, baby.
让我们查一下msdn里system.net configuration element的文档(因为webrequest类在system.net里),有4个child element:
<authenticationModules> Specifies the modules used to authenticate Internet requests.
<connectionManagement> Specifies the maximum number of connections to Internet hosts.
<defaultProxy> Specifies the proxy server used for HTTP requests to the Internet.
<webRequestModules> Specifies the modules used to request information from Internet hosts.
找到了,从connectionManagement里你可以看到如何设置maxConnection #,然后最低下有指向ServicePoint和ServicePointManager的连接,那里,有如何设置pooling的介绍。
Well, quite an adventure, isn't it? I mean, it really doesn't have to take that much if you know system.net.connectionManagement element from the every beginning. I just found this process of following the cues, digging around, pondering about things, etc is quite some fun and definitely worth the time, : )
At least, for me, it is.
Hope you enjoyed it.
========================================================
升级后没有了code highlighter真不方便啊,code snippet在我的主页上看好好的,在博客堂的主页上看就很乱。
【原文地址】 Tip/Trick: Implement "Donut Caching" with the ASP.NET 2.0 Output Cache Substitution Feature
【原文发表日期】 Tuesday, November 28, 2006 12:17 AM
一些背景:
ASP.NET中一个强大无比,但往往未被充分利用的功能是它丰富的缓存设施。ASP.NET的缓存功能允许你在服务端避免为来自客户端的每一个新请求做重复的工作,而是,你可以一次生成HTML内容或数据结构,然后在服务器端ASP.NET中缓存或存储其结果,在以后的web请求中重用这些结果。这可以极大地提高你应用的性能,降低对象数据库这样的关键后台资源的负载。
Steve Smith 几年前曾在MSDN上写过一篇很好的关于ASP.NET 1.1 中缓存的文章,讨论了ASP.NET 1.1 缓存功能的一些基本知识,并且对如何使用它们提供了一个很好的总结。如果你以前从没有用过ASP.NET 缓存的话,我建议你先读一下这篇文章,并对其中的每个特性都尝试一下。我也非常建议你观看一下ASP.NET 2.0 免费录像系列中的这个15分钟的关于ASP.NET缓存的“How Do I”录像,这个录像实战演示了ASP.NET 缓存。
ASP.NET 2.0添加了2个非常重要的改进,使得缓存功能更加完善:
1) 对SQL缓存失效的支持 - 这允许你在缓存的页面或数据结构所依赖的数据表或记录行被更新时,使缓存内容自动失效然后重新生成缓存内容。例如,你可以在一个电子商务网站上输出缓存你所有的产品列表网页,然后确信在数据库中的产品价格一旦有所变动,这些网页就会在下一个请求时重新生成,这样就不会向用户显示过期的价格数据了。
2) 输出缓存的替换 - 这个奇妙的特性允许你实现我有时称之为“甜圈缓存(donut caching)” 的功能,在这里,你输出缓存页面上的所有东西,但除了几个包含在缓存区域内的动态区域外。这允许你更积极地实现整页输出缓存,不用为了实现局部页面缓存而把你的页面分成多个.ascx用户文件。下面这个技巧/诀窍指南更好地解释了这个特性的促动因素以及其实现。
实战中的场景:
你要在你的网站上实现一个产品列单网页,在上面列出在某个指定产品分类下的所有产品。你也想要输出缓存这个网页,这样,你就不用在每次请求时都访问数据库。你在Products.aspx 网页的顶部用声明的方式添加一个 <%@ OutputCache %> 指令就可以很轻松地达成这个目的,该网页上包含一个绑定到从你的中间层返回的产品数据的 <asp:datalist> 控件。
注意下面该网页是如何设置输出缓存它的内容 100,000 秒或者直到northwind数据库中的 products数据表为新的价格数据所更新为止,在后面这个情形下,下一个请求时,它就会重新生成页面。OutputCache 指令还有一个VaryByParam 属性,它告诉 ASP.NET 为每个独特的categoryID 单独储存一份缓存页面。譬如,Products.aspx?categoryId=1,Products.aspx?categoryId=2等等各有一个单独的缓存页面。
Products.aspx:
<%@ Page Language="VB" MasterPageFile="~/Site.master" AutoEventWireup="false" CodeFile="Products.aspx.vb" Inherits="Products" %>
<%@ OutputCache Duration="100000" VaryByParam="CategoryID" SqlDependency="northwind:products" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<div class="catalogue">
<asp:DataList ID="DataList1" RepeatColumns="2" runat="server">
<ItemTemplate>
<div class="productimage">
<img src="images/productimage.gif" />
</div>
<div class="productdetails">
<div class="ProductListHead">
<%#Eval("ProductName")%>
</div>
<span class="ProductListItem">
<strong>Price:</strong>
<%# Eval("UnitPrice", "{0:c}") %>
</span>
</div>
</ItemTemplate>
</asp:DataList>
<h3>Generated @ <%=Now.ToLongTimeString()%></h3>
</div>
</asp:Content>
Products.aspx.vb:
Partial Class Products
Inherits System.Web.UI.Page
Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim products As New NorthwindTableAdapters.ProductsTableAdapter
DataList1.DataSource = products.GetProductsByCategoryID(Request.QueryString("categoryId"))
DataList1.DataBind()
End Sub
End Class
浏览器访问时,从服务器端返回下面这个页面:
注意,页面底部的时间戳每隔100,000秒,或者当products数据表里的价格数据被更新时,才会被更新。它将会被缓存起来以应付所有其他的 HTTP 请求,允许我们在生产服务器上每秒处理1000个请求,避免了不必要的数据库访问,从而使得访问的速度极快。
问题:
我们在上面的例子中将会遇到的一个问题,跟我们在页面右上方输出的欢迎消息和用户名字(在上面红圈中)有关。目前这是在我们的Site.Master母板页文件中通过使用一个新的ASP.NET 2.0 <asp:loginname> 控件来生成的,象这样:
<div class="header">
<h1>Caching Samples</h1>
<div class="loginstatus">
<asp:LoginName FormatString="Welcome {0}!" runat="server" />
</div>
</div>
我们将撞上的问题是,因为我们给我们的页面加了整页输出缓存,第一个访问网站的用户的名字将被保存到页面的缓存输出中,这意味着,在默认情形下,在初始请求之后的 100,000 秒之内访问网站的用户将收到一个错误的欢迎消息,更糟糕的是,显示的是错误的用户名字!
解决方案:
有2个方法可以解决这个问题。
第一个方案是将整个页面做成动态的,即去除顶层的 <%@ OutputCache %> 指令,对页面的内容重构,把所有可缓存的内容都封装在ASP.NET用户控件中,这些用户控件是通过 .ascx 文件来实现的。然后你在这些用户控件的每一个文件的顶部添加 <%@ OutputCache %> 指令,使得它们可以单独缓存。这避免了每次请求都需要访问数据库,确保了用户名字总是正确地输出的,因为用户名字不在缓存的用户控件区域里。这个方法目前在ASP.NET 1.1 里就可行,当然,在ASP.NET 2.0依然行之有效。
但这个方案的缺点是,为使缓存可行,它要求我们重构我们页面的编码和布局。但假如我们在页面上只有几个地方我们想要保持动态,这个重构会非常不方便。好消息是,ASP.NET 2.0 增加了对输出缓存替换块(Output Cache Substitution block )的支持,它提供了一个极其干净的方法来处理这个场景。
使用 <asp:substitution>控件的输出缓存替换块:
输出缓存替换块允许你OutputCache整个页面的输出,同时在HTML输出中留下几个动态区域标记来指明在以后的请求中你需要动态填充内容的地方(譬如,上面例子中的用户名消息)。我有时把这称为“甜圈缓存(donut caching)功能”,因为外部的页面内容都是缓存的,只有页面内容流中间的几个孔(hole)是动态的。这与使用用户控件实现的局部页面缓存正好相反,因为在局部页面缓存的情形下,整个页面是动态的,中间的区域是缓存的。
你通过使用整页输出缓存的方式来实现输出缓存替换,使用与上面 Products.aspx 例子中完全一样的句法。然后,你可以通过在页面上添加 <asp:substitution> 控件来指明页面的哪些区域你需要动态地使用替换块来填充,象这样:
<div class="header">
<h1>Caching Samples</h1>
<div class="loginstatus">
<asp:Substitution ID="Substitution1" runat="server" MethodName="LoginText" />
</div>
</div>
<asp:Substitution> 控件与ASP.NET中的其他控件不同,它与 ASP.NET 的输出缓存注册了一个回调事件,当页面内容在后来的请求中从 ASP.NET输出缓存发出时,该事件会导致你的页面或母板页的一个静态方法的调用。这个静态方法在运行时会传进一个HttpContext 对象,它包含了ASP.NET Request, Response,User, Server,Session, Application等内在对象,然后你就可以使用它们来返回一个字符串,ASP.NET 会在内容发回客户端前自动把这个字符串注入到页面的相关区域里去。
譬如,在输出缓存的products.aspx 页面中,为处理上面这个我们需要动态输出欢迎消息的场景,我们只要简单地添加这个方法到我们的Site.Master后台代码文件中,然后让上面这个 <asp:substitution> 控件来调用:
Partial Class Site
Inherits System.Web.UI.MasterPage
Shared Function LoginText(ByVal Context As HttpContext) As String
Return "Hello " & Context.User.Identity.Name
End Function
End Class
这样,整个页面将被输出缓存,除了我们页面右上方的 <asp:substitution> 控件代表的欢迎消息的内容外。
很明显地,我们可以把这个进一步延伸,假如我们想要包括另外象用户他们的购物篮有多少样东西这样个人化的信息等。非常酷的是,页面上所有其他的内容仍然是保持完全缓存的,我们不用在后继请求里访问数据库来生成其内容,这意味着我们在单独一个服务器上每秒钟就可以处理成千个产品页。实际上,在请求中,不用生成页面上的任何控件,在以后的请求里,除了上面那个静态方法外,也没有编码会执行,这使得一切都快速无比。
使用Response.WriteSubstitution 方法的输出缓存替换块:
除了使用 <asp:substitution> 控件在页面上指定可替换的内容块外,你也可以使用 Response.WriteSubstitution 方法。这个方法接受一个HttpResponseSubstitutionCallback 方法的delegate对象为参数,你可以在你的应用的任何类里实现这个方法 (而不限于你后台类里的静态方法)。
<asp:substitution> 控件在内部使用这个方法来接连页面的后台类里的delegate方法。同样地,你也可以在你自己的控件或页面使用这个方法,以取得最大的控制和灵活性。
结论:
我还没有找到一个无法从ASP.NET缓存功能上受益的ASP.NET应用。因为ASP.NET支持整页输出缓存,局部页面输出缓存,以及现在的甜圈(donut)层次的缓存,这允许你根据任何参数或你需要的自定义逻辑来变换缓存内容,而现在又允许你在数据库改变时自动使得缓存内容失效并重新生成缓存内容,你不应该发现你自己再会建造一个用不上任何缓存的应用了。
我绝对建议你花点时间熟悉一下ASP.NET所有的缓存功能。想找到我完成的有关缓存的另外的例子的话,请下载我最近在ASP.NET Connections大会上做的技巧/诀窍讲座。下载内包括讲义和演示代码,说明如何使用整页缓存,局部页面缓存,替换块缓存(substitution block caching),以及SQL缓存失效(SQL Cache Invalidation)。
想阅读我写的其他ASP.NET技巧/诀窍博客帖子的话,请浏览我的ASP.NET技巧,诀窍和资源网页。
希望本文对你有所帮助,
Scott
在上一篇blog: 关于定位 中, 我提到关于我们的定位, 可以从 人->商业人员->IT业职员(软件工程师) 这样的逻辑关系来考虑. 那么今天我们先来看看"人"这一部分.
正如老话说的一样:"做事先做人", 其实无论你从事甚么样的职业(甚至没有工作), 为人正直成熟也是身为文明人最基本的要求. 一名好的IT业职员首先要得是一名好的商业人员, 而一名好的商业人员首先得是一个成熟的自然人, 见图一.

(图一)
纵观全世界各个文化, 虽然它们历史演变的背景都不同, 但倡导的道德都包括: 善良, 正直, 勇气等等这类共通因素. 例如中国传统美德中, 孔子、孟子讲讲忠、孝、仁、义、礼、智等。孟子谈论四端说:“恻隐之心,仁之端也;羞恶之心,义之端也;辞让之心,礼之端也;是非之心,智之端也。”《中庸》所讲的三达德为智、仁、勇。而在西方的Bible中, 《加拉太书》(Galatians) 5:22-23说:“圣灵所结的果子,就是仁爱、喜乐、和平、忍耐、恩慈、良善、信实、温柔、节制。……”
企业文化很多也是以上的社会道德准则演化而来, 越是成熟的企业对其人员道德的重视程度也越高(例如HP的商业执行准则SBC就体现了坚持"絕不妥協的誠信經營企業"的信念. SBC就好像HP的宪章一样, 而违反的下场一般就是be fired). 作为企业的一名雇员, 你的价值观越于企业的文化达成一致, 你得到的提升机会也就越大.

(图二)
如图二所示, "High业绩High企业文化符合度"自然是最佳; "Low业绩Low企业文化符合度"肯定是被淘汰. 那么"High业绩Low企业文化符合度"呢, 从短时间来说还ok, 但是长远下去肯定不行; 至于"Low业绩High企业文化符合度", 则可以成为企业培训的目标.
其实, 关于怎么样做人这个topic实在太大, 完全超过了这里讨论的范畴了, 每个人都有自己的人生观价值观, 我不敢妄自说哪种是对的那种是错的, 这个问题还是交给各位读者自己了. 如何作一个善良,正直勇敢的人, 可以说是一生的课程. :)
不过有一些个人体会linkcd可以share一下给大家: 去制定自己的使命宣言, 也就好像是制定自己的人生宪法一样. 关于如何写, 网上的资料其实很多, 而且每个人的情况不同, 制定好的宣言也是因人而异. 在linkcd的宣言里面包含的有以下几点, 给大家参考吧:
===
(注:有省略)
目标:
更精彩更完整的体验人生
宣言:
作为一个成年人, 我想要成为,并且致力成为具有以下特质的人:
对待生活积极主动, 不无谓浪费时间
第一时间承认错误
勇于承担责任, 而不是逃避
自律, 信守诺言
自信, 并保持谦和
终生学习
财务自由
身体健康
作为一个职业人士, 我想要:
从事自己喜欢的职业, 了解自己的长处,发展自己的才能, 保证自己的核心竞争力
===
下一段胡扯内容的预告: 如何作一个好的商业人员.
后记: 关于系列(一):关于定位那篇blog中, 笔者犯了一个错误: 例子二(Fred和Bob的例子)太过于抽象和主观性了, 导致得到很多批评, 也偏离了笔者的原意. 其实我并不是要将technical skill和profit making对立起来, 但需要注意的是不要把我们的目标 (profit making) 和 实现目标的手段 (technical skill) 完全等同起来. 要实现盈利的目标, 我们还需要思考除了单纯技术以外的东西.
博客堂目前已经成功升级到ASP.NET 2.0,在未来我们还将增加一些.NET 3.0的特性支持,并且将会定期发布源代码版本以及安装版本给所有博客堂的朋友。同时也为了更好的开发博客堂源代码,使其更加高效、安全、稳定、丰富,我们现在向所有博客堂的堂主以及众读者招募志愿者,加入开发者系列。有意参加博客堂源代码开发的朋友,可以至http://www.codeplex.com上注册帐号,然后通过邮件(joycode # live.com,主题上注明:博客堂志愿者字样)将您的帐号告知我们,同时发出一份个人简介(博主们可以通过我的Windows Live Messenger告知)。
所有志愿者需要配置安装运行环境,环境要求如下:
- 操作系统:Windows XP+SP2、Windows Server 2003+SP1, Windows Vista(在VS2005 SP1发布前不推荐使用该系统进行开发);
- 数据库: Microsoft SQL Server 2000或者Microsoft SQL Server 2005或者Visual Studio自带的SQL Express 2005;
- 开发工具:Visual Studio 2005 Team Developer或者以上版本(需要安装Team Foundation Explorer,安装方式Mango会向大家介绍),如果申请主要从事数据库优化者,可以安装Visual Studio 2005 Team DBA版本,如申请设计人员,请安装Microsoft Expression Web试用版;
- 上网条件:家中有ADSL上网条件;
我们需要招募三类志愿人员,所需条件如下:
- 开发人员
- 了解.Text 0.95架构,曾经安装或者阅读或者改写过.Text 0.95代码(必须);
- 熟悉ASP.NET 2.0的各种新特性(必须);
- 熟悉ASP.NET Ajax Beta 2(或以上)的新特性;
- 设计人员
- 了解HTML以及CSS相关知识(必须);
- 可以使用Microsoft Expression Web版本进行网页开发设计;(必须,主要是为了使用TFC与服务器端相连)
- 熟悉XAML以及WPF相关知识(非必须,但有此技能将会增加您成功的机会);
- 熟悉.Text Skin开发设计或者ASP.NET 2.0页面设计;
- 数据库优化人员
- 了解Microsoft SQL Server 2000以及Microsoft SQL Server 2005(必须);
- 了解.Text 0.95数据库结构;
- 掌握丰富的数据库优化的技能;
另外,所有以上开发人员要求以下条件:
- 富有团队合作能力以及保密性;
- 拥有足够的时间进行夜间工作;
博客堂源代码开放项目主页设置在http://www.codeplex.com/joycode,欢迎大家参观。
More Posts
Next page »