置顶随笔
#
摘要: 昨天在网上看到天龙书局在卖 《ASP.NET 本质论》,繁体版的封面还是很漂亮的。 ASP.NET 本質論郝冠軍 著、賴榮樞 譯出版商: 碁峰出版日期: 2011-09-08台幣定價: $520售價: 8.0 折 $416語言: 繁體中文頁數: 504ISBN: 9862762799EAN: 9789862762790立即出貨產品描述<內容特色>‧以最新ASP.NET為基礎,全面深入剖析ASP.NET的本質‧資深ASP.NET專家執筆,微軟技術社群和MVP聯袂推薦ASP.NET透過一整套封裝了底層處理機制的類别庫提供極其高效率的開發環境,使得許多開發工作透過簡單的控制項拖曳就可以實
阅读全文
摘要: 《ASP.NET 本质论》这本书马上就要上市了,博客园对这本书的出版提供了成长的土壤和营养,更要感谢各位朋友对我的帮助和支持。《ASP.NET 本质论》中没有提供光盘,源码可以在这里直接下载。直接下载全部示例代码: 点击下载也可以针对章节内容,分别下载示例代码:第 1 章 网站应用程序第 2 章 应用程序对象第 3 章 处理请求的七种武器第 4 章 ASP.NET 中的线程与异步第 5 章 页面即对象第 6 章 状态第 7 章 模板和数据绑定第 8 章 自定义控件第 9 章 MVC第 10 章 IIS 与 ASP.NET本书可以在互动出版网直接订购,订购地址:http://www.china-
阅读全文
摘要: 本书的起源经常有人问起:应该如何学习 ASP.NET 开发?为什么开始的时候感觉很容易,但是,遇到问题的时候却感到无从下手?太多的人开始学习的时候,对 ASP.NET 有着深深的误解,包括我自己。 很多人选择 ASP.NET 的理由是因为它简单:中文开发环境、简体中文的文档、简单的拖放式开发、类似于 WinForm 的开发体验等。Visual Studio 和 .NET Framework为我们提供了一个极其方便的开发环境,很多人因此进入了 ASP.NET 开发之门,甚至有相当多的 ASP.NET 程序员都没有了解过 HTTP 协议的内容,或者 HTML 的语法,也同样在完成着开发任务。 这究
阅读全文
2012年2月7日
#
看到这样的一道面试题:
int i = 10;
object obj = i;
int j = (int) obj;
分析一下程序执行中的内存处理。
首先,我们可以看到这段程序定义了三个局部变量,局部变量将被定义在栈中,第一个变量比较简单,由于 i 是整形变量,所以变量 i 的值直接被保存在堆栈中。
而第二行对 obj 的赋值要复杂一点点,由于 obj 的类型是 object 类型,这是引用类型,所以,在堆栈中保存的必须是一个对象的引用,而不能是一个值,此时,会发生著名的装箱,CLR 会在堆中创建一个对象,在这个对象中保存变量 i 的值,并且,还会同时保存这个值的类型,这里是整数类型。此时,在内存中将会存在两个 10,一个保存在栈中,一个保存在堆中。
有的人认为此时堆中对象中保存的值就是引用堆栈中 j 的值,这个问题我们可以验证一下,如果是这样的话,我们修改 i 的值,那么 obj 应该也会同时发生变化。我们可以在第二行之后增加两行检查一下i。
i = 99;
Console.WriteLine( obj );
你可以看一看 obj 是否发生了变化。
第三行执行的时候,将一个引用类型的对象赋予一个值类型的变量 j,这时,会发生另外一个著名的事件:拆箱,CLR 会检查拆箱的类型是否匹配,然后从堆的对象中读取保存在其中的值,将这个值保存到位于堆栈中的变量 j 中。至此,在内存中已经存在了三个 10,两个在堆栈中,一个在堆中。如图所示:

如果我们为上面的程序再增加一行:
那么,又会发生什么情况呢?
有的人认为在堆中其实还是第二行中装箱的对象,此时的 obj2 引用的也是这个对象,内存中的结构如下所示:

验证这个问题稍微复杂了一点,因为我们不能为 obj 赋值,赋值的话就是另外一个对象。而这个装箱的对象也没有什么方法让我们在不改变对象的情况下仅仅修改其中的值。
不过,我们还是有办法来检查它们是否为同一个对象的。
首先,我们可以考虑对象的 HashCode,在 Object 基类上定义了 GetHashCode 方法,可以返回对象的 Hash 码,通常我们可以通过比较对象的 HashCode 来判断引用的是否为同一个对象。那么,我们可以使用下面的语句来检查一下。
Console.WriteLine( obj.GetHashCode() );
Console.WriteLine( obj2.GetHashCode() );
不过,很不幸的是,你会看到输出了两次 10,那么,它们是同一个对象吗?我们还不能这么说。
HashCode 的用途主要用在对象作为字典的键的时候,用来判断键是否相同,但是,有的时候,对于不同的对象我们也希望他们看作同样的键,因此这个 GetHashCode 方法实际上是虚方法,是可以被类型重写掉的。在整数类型中,这个方法已经被重写掉了,所以,你会看到具有同样值得整数返回的 HashCode 是相同的。
比如,你有两张 10 元的人民币,这两张人民币显然不是同一张,但是,他们的票面价格是相同的,无论你用那一张存到银行中,银行都会为你记下 10 元的存款,这称为值相等,也就是价值相同。Object 基类还定义了方法 Equals ,这个 Equals 方法就是用来判断值相等的。所以,在重写了 GetHashCode 方法之后,你应该也重写一下 Equals 方法,以保证值相等的比较。
回到我们的问题,我们显然需要判断的不是值相等,还有什么方法吗?有,Object 基类还有一个静态方法 ReferenceEquls,专门用来比较两个对象是否其实是同一个对象实例,这称为引用相等。因此,下面的代码可以轻易地用来完成这个任务。
Console.WriteLine( Object.ReferenceEquals( obj, obj2 ) );
问题进行到这里,已经基本完成了。不过,CLR 又是怎么知道这两个对象是否为同一个对象的呢?在系统内部,每个对象在应用程序域中一旦诞生,CLR 都会赋予一个唯一的 HashCode ,虽然我们可以重写 GetHashCode,但是,这个内部的 HashCode 还是存在的,定义在命名空间 System.Runtime.ComilerServices 命名空间中的类 RuntimeHelpers 助手类就可以帮助我们解决这个问题,它的静态方法 GetHashCode 可以接受一个对象的引用,返回这个对象底层的 HashCode。
使用下面的代码,我们可以看到两个对象的 HashCode 真的是不同的。
Console.WriteLine(System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj));
Console.WriteLine(System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj2));
实际的内存结构应该如下:

完整的代码如下所示:
int i = 10;
object obj = i;
int j = (int)obj;
object obj2 = i;
Console.WriteLine(obj.GetHashCode());
Console.WriteLine(obj2.GetHashCode());
Console.WriteLine(Object.ReferenceEquals(obj, obj2));
Console.WriteLine(System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj));
Console.WriteLine(System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj2));
2012年1月29日
#
原文地址:http://social.msdn.microsoft.com/Forums/zh-CN/silverlightzhchs/thread/25d7a00f-7f08-4630-a6e7-002edd2877c7
注:我参考原文配置了 Silverlight 的开发环境,但是原文的图片不能显示了,这里转载一下,顺便配上图片。
Silverlight 5已于美国时间2011年12月9日正式发布,其在 Silverlight 4 的基础上新增了 40 多个新功能,完善了媒体支持并提供了更加丰富了用户界面。这个版本带来了很多改进,大家可以通过官方列表了解详情,或者点击此网页查看来自Pete Brown的多个视频介绍。
下面是对Silverlight 5新增功能的简要总结:
- 强数据绑定功能,包括上级绑定、隐式数据模板、自定义标签扩展等
- 新增3D图形类库,主要引入轻量级XNA 3D功能,该框架目前是XBOX游戏的主要开发框架,这也使得3D图形可以轻松地在Web网页中呈现
- 改进多媒体功能,包括使用XNA的声效库、支持H.264媒体硬件解码、变速播放、遥控器的按键支持(主要针对Live TV)等
- 增强文本功能,包括文本清晰度优化、文本块间漂移、矢量打印等
- 新增与操作系统的集成,包括支持P/Invoke调用外部程序、多窗体、在完全信任模式下无限制的文件系统访问、支持SaveFileDialog和OpenFileDialog的默认文件名、64位浏览器支持等
- 优化整体性能,包括网络延迟的改进、使用TPL并行任务、解析器的性能改进、为改善启动时间的多核心JIT等
- 增强控件,包括双击与多击的支持、加入PivotViewer、ComboBox增强等
- 提高开发效率,包括XAML调试与数据绑定调试、团队测试的支持等
现在,Silverlight开发者可以点击下载正式版Silverlight 5 Tools和Silverlight 5 Toolkit(12月版)。可惜的是目前官方只发布了英文版开发包,按照惯例中文版一般要等到3-5个月之后才会发布,如果您使用的Visual Studio 2010是中文版,在安装时会出现语言不符的提示,但我们有办法将英文版开发包安装到中文开发环境中。下面是如何搭建Silverlight 5中文开发环境的具体步骤:
1.首先确认您的Visual Studio 2010已经升级到SP1
2.解压下载下来的Silverlight5_Tools.exe(winrar 或者7-zip均可);

3.在解压后的目录中,打开ParameterInfo.xml,替换所有1033为2052,保存;
4.运行同目录下SPInstaller.exe 安装Silverlight5 Tools;

5.通常装完后,在VS中新建项目仍然不能使用Silverlight5,提示需要安装Silverlight4 Tools,此时需要修改注册表,在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\10.0\Setup\VS\BuildNumber下,将 "2052" 的值修改为 "10.0.30319",然后安装Silverlight 4 Tools中文版程序(可以到这里下载);
不过,在我这里,安装完成之后就已经没有问题了。
6.安装完Silverlight 4 Tools后,将上述注册表中中修改的 “2052”值还原为“10.0.40219”。
通常如果您需要设计Silverlight应用,还需要使用Blend来进行界面设计,对应Silverlight5的版本请点击这里下载。现在,我们就可以尽情体验Silverlight 5给我们带来的新特性了。
笔者在新书中精心制作了Silverlight 5的新功能示例,大家可以点击此示例网页来体验。
注意:部分示例需要安装后才能查看,对于3D示例需要在Silverlight右键的“权限”选项卡中允许3D显示,如下图。
2012年1月17日
#
根据 W3C DOM 2 Events 描述,EventTarget 接口被所有支持 DOM 事件模型的节点(Node)实现。 该接口提供了 'addEventListener' 和 'removeEventListener' 方法,用来绑定或解绑一个 EventListeners 接口到一个 EventTarget。
DOM 2 Events 中定义了 Event 接口,用来提供事件的上下文信息,它提供了若干标准属性和方法。 实现 Event 接口的对象一般作为第一个参数传入事件处理函数,以用来提供当前事件相关的一些信息。
事件注册
根据 DOM 2 Events 中描述,节点使用 'addEventListener' 和 'removeEventListener' 方法绑定和解绑事件监听器,但 IE6 IE7 IE8 不支持这两个方法, 而使用 'attachEvent' 和 'detachEvent' 方法作为替代方案,Opera 两类方法都支持。Chrome Safari Firefox 只支持标准方法。
为了解决浏览器兼容问题,可以自定义函数来解决。例如:
var EventUtil = {
addHandler: function (element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
},
removeHandler: function (element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else if (element.detachEvent) {
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
}
};
关于 'addEventListener' 和 'attachEvent' 有几点需要注意:
- IE 不支持在捕获阶段触发事件监听器,'attachEvent' 方法没有提供参数说明是否响应在捕获阶段触发的事件;
- 'addEventListener' 和 'attachEvent' 都可以注册多个事件监听器;
- 在 Firefox Chrome Safari Opera 中给同一事件注册同一个事件监听器多次,重复注册的会被丢弃;而在 IE 中重复注册的事件监听器会被重复执行多次;
- 当给同一元素注册了多个事件监听器的时候,IE6 IE7 的事件监听器执行顺序是随机的,IE8 是倒序的,Firefox Chrome Safari Opera 是顺序的;
- 当元素注册的事件监听器中有非法的事件监听器时(非函数),在 IE Firefox 中会抛出异常,而在 Chrome Safari Opera 中则会忽略非法的事件监听器,继续执行其他的事件监听器。
事件对象
在ie中,事件对象是作为一个全局变量来保存和维护的。 所有的浏览器事件,不管是用户触发的,还是其他事件, 都会更新window.event 对象。 所以在代码中,只要轻松调用 window.event 就可以轻松获取 事件对象, 再 event.srcElement 就可以取得触发事件的元素进行进一步处理。
对于标准的 DOM 处理来说, 事件对象却不是全局对象,一般情况下,是现场发生,现场使用,把事件对象自动传递给对应的事件处理函数。 在代码中,函数的第一个参数就是事件对象了。
为了解决兼容性问题,通常在代码中如下处理:
function handler(e){
e = e || window.event;
}
需要注意的是,使用 <button id="btn" onclick="foo()">按钮1</button> 进行事件注册,标准方式下却不能在事件处理方法中取得事件对象。
原因是 onclick="foo()" 就是直接执行了, foo() 函数,没有任何参数传递给 foo 函数。
有两个办法解决这个问题。
第一,将注册的方法修改为 <button id="btn" onclick="foo(event)">按钮</button>,注意,这里的 event 不是形参,而是实参,必须名为 event。这样 foo 函数就可以得到事件参数了。
第二,不修改注册的代码,在事件处理方法上进行处理。关键在于此时实际上存在事件对象,只不过没有传递给 foo 函数罢了,我们可以找到调用 foo 函数的那个函数,当然这是一个系统函数,没有关系,通过 foo.caller 可以取得当前调用 foo 函数的函数,这个函数的第一个参数就是事件对象,所以,我们可以这样取得这个事件对象了。foo.caller.arguments[0]。
注意:
- 只有在使用 attachEvent 方法注册事件监听器的时候,IE 才支持使用事件监听器传入的第一个参数作为事件对象的方式;
- Chrome Safari Opera 两种获取事件对象的方式都支持;
- Firefox 只支持获取事件对象的标准方式。
事件对象的属性
IE 对事件对象的标准属性和方法支持有限,针对大部分属性和方法,IE 都提供了一套替代非标准的替代方案; 而 Firefox Chrome Safari Opera 除了全面支持事件对象的标准属性和方法外,还在不同程度上支持了 IE 提供的非标准替代方案。
使用特性判断使用与标准对应的非标准方法及属性
target srcElement
preventDefault() returnValue
stopPropagation() cancelBubble
relatedTarget fromElement toElement
例如:
getEvent: function (event) {
return event ? event : window.event;
},
getTarget: function (event) {
return event.target || event.srcElement;
},
preventDefault: function (event) {
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
},
stopPropagation: function (event) {
if (event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
}
参考资料:
SD9011: 事件模型在各浏览器中存在差异
2011年12月15日
#
通过 WCF 与页面进行 Ajax 进行数据传递非常方便,可以,遇到日期类型就不同了。
WCF 通过 JavaScriptSerializer 将日期格式化为特殊的格式:\/Date(1318287600+0100)\/,实际上传递到页面上的是一个字符串。而不是真正的 JavaScript 日期。
同样,当浏览器想要向服务器传递日期类型的数据时也必须通过这种特殊的字符串来提供数据。
为了解决这个问题,可以使用下面的方法为 jQuery 扩展一个数据转换的方法。
// 为 jQuery 扩展一个解析 wcf 日期的方法
jQuery.extend(
{
wcfDate2JsDate: function (wcfDate) {
var date = new Date(parseInt(wcfDate.substring(6)));
return date;
},
jsDate2WcfDate: function (jsDate) {
// \/Date(568310400000+0800)\/
return "\/Date(" + jsDate.getTime() + "+0000)\/";
}
}
);
在页面中引用 jQuery 脚本文件之后,加入这段脚本即可。
可以如下方式来使用:
var d = new Date(); // 标准的 JavaScript 日期数据
alert(d);
var wcf = $.jsDate2WcfDate(d); // 转换为 WCF 日期格式
alert(wcf);
alert($.wcfDate2JsDate(wcf)); // WCF 日期格式转化为 JavaScript 日期格式
当然,可以将这段脚本保存在一个文件中,以后在页面中直接引用即可。
下载脚本文件
2011年12月1日
#
JSON 格式
json 是 Ajax 中使用频率最高的数据格式,在浏览器和服务器中之间的通讯可离不开它。
JSON 的格式说明可以在可以这里看到,非常详细,还是中文的。
JSON 格式说明
需要特别注意的是,在 JSON 中的属性名是需要使用引号引起来的。
jQuery 中使用 JSON
jQuery 是现在使用广泛的脚本库,那么,在 jQuery 中如何使用 JSON 呢?

解析 JSON
在 jQuery 中已经提供了对于解析 JSON 的内在支持,
jQuery.parseJSON 函数提供了解析的支持,详细的说明见这里。
var obj = jQuery.parseJSON('{"name":"John"}');
alert( obj.name === "John" );
使用对象生成 JSON 格式串
在 jQuery 中并没有提供直接将普通的 JavaScript 对象转换为 JSON 串的方法,可以使用下面的扩展库来完成。
jquery-json 扩展库
这个库用来扩展 jQuery ,对于 JSON 的使用,扩展了两个方法。
toJSON 方法用来将一个普通的 JavaScript 对象序列化为 JSON 串。
var thing = {plugin: 'jquery-json', version: 2.3};
var encoded = $.toJSON( thing ); // '{"plugin":"jquery-json","version":2.3}'
evalJSON 方法将一个 JSON 串解析为一个普通的 JavaScript 对象。
var thing = {plugin: 'jquery-json', version: 2.3};
var encoded = $.toJSON( thing ); // '{"plugin":"jquery-json","version":2.3}'
var name = $.evalJSON( encoded ).plugin; // "jquery-json"
var version = $.evalJSON(encoded).version; // 2.3
这个扩展的下载地址:http://code.google.com/p/jquery-json/
使用 jQuery 配合 WCF
客户端
jQuery 中的 $.post 可以直接向服务器发出请求,将服务器返回的数据按照 JSON 方式进行解析,不过,需要注意下面几点:
请求的内容类型必须为 json 格式,这可以通过上面的 jQuery-json 扩展库来完成,需要特别注意的在请求的 contentType 也必须使用 text/json 进行说明,默认的 post 使用普通的名值对方式请求,因此 contentType 是: application/x-www-form-urlencoded,可以通过 $.ajaxSetup 来进行设置:
// Ajax 设置
$.ajaxSetup({ contentType: 'text/json' });
这样,请求的内容类型就设置为需要的类型。
其次,实际的请求内容必须使用 JSON 方式,这可以通过扩展库的 $.toJSON 来实现,例如:
这样,如果服务器端提供了一个服务方法 Sum,定义如下:
public int Sum(int x, int y)
{
return x + y;
}
就可以如下调用了。注意,WCF 返回的数据在属性 d 中。
// Ajax 设置
$.ajaxSetup({ contentType: 'text/json' });
$("#wcfBtn").click(function () {
$.post("Service1.svc/Sum", $.toJSON({ x: 2, y: 3 }), function (data) {
alert(data.d);
});
});
服务器端的配置
首先,为服务增加标签:[System.ServiceModel.Activation.AspNetCompatibilityRequirements(
RequirementsMode = System.ServiceModel.Activation.AspNetCompatibilityRequirementsMode.Allowed)]
然后,在网站的配置文件中,如下配置。
<system.serviceModel>
<!-- 为了支持在浏览器端调用 WCF 服务的特定配置 -->
<serviceHostingEnvironment aspNetCompatibilityEnabled="true">
<serviceActivations>
<!-- relativeAddress 服务的地址
service 实现服务的类型,全名,包含命名空间,甚至程序集
factory 是 WCF 系统提供,直接使用
-->
<add relativeAddress="Service1.svc" service="MServer.Service1" factory="System.ServiceModel.Activation.WebScriptServiceHostFactory"
/>
</serviceActivations>
</serviceHostingEnvironment>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
2011年11月23日
#
摘要: 这一个系列的内容来自微软的音乐商店 Music Store, 这是项目在 Codeplex 上的地址:http://mvcmusicstore.codeplex.com/。这个项目使用 ASP.NET MVC3 完成,项目中除了提供完整的示例代码之外,还提供一个 100 多页的说明文档,这里的内容就翻译自这篇文档。在我学习这个项目的过程中,发现有一些地方与文档并不完全一致,项目中也引入了一些新的软件,在第一次使用的时候会比较陌生,因此,将整个项目使用中文 VS2010 重新完成一遍,将原文的界面全部替换为使用中文 VS2010 操作的界面,这样对大家可能会有一些帮助。ASP.NET MVC 音
阅读全文
2011年11月21日
#
摘要: 我们已经完成了网站的大部分工作,但是,还有一些添加到站点的导航功能,主页,以及商店的浏览页面。创建购物车汇总部分视图我们希望在整个站点的页面上都可以看到购物车中的数量。 通过创建一个部分视图,然后添加到网站的布局中就可以容易地完成,前面看到,在 ShoppingCart 控制器中包含了一个名为 CartSummary 的 Action 方法返回分部视图。//// GET: /ShoppingCart/CartSummary[ChildActionOnly]public ActionResult CartSummary(){ var cart = ShoppingCar...
阅读全文
2011年11月20日
#
摘要: 在这一节,我们将创建结账的控制器 CheckoutController 来收集用户的地址和付款信息,我们需要用户在结账前注册账户,因为这个控制器需要授权。当用户点击结账 Checkout 按钮的时候,用户将会被导航到结账的处理流程中。如果用户没有登录,将会被提示需要登录。一旦用户成功登陆,用户就可以看到地址和付款的视图。一旦用户填写了这个表单并提交,他们将会看到订单的确认页面。视图访问不存在的订单,或者不属于你的订单,将会看到错误页面。合并购物车在匿名购物的时候,当用户点击结账 Checkout 按钮,用户会被要求注册和登陆,用户会希望继续使用原来的购物车,所以,在匿名用户登录之后,我们需要维
阅读全文
摘要: 示例查询最多的应用场合是组合查询,我们常常需要在界面上提供若干的查询选项,然后根据用户的输入返回符合条件的结果。使用代码直接进行处理往往需要涉及到复杂的条件,由于组合条件并不确定,导致逻辑判断语句结构复杂。对于多个可选的参数,情况会变得更加严重。使用示例查询可以很方便地处理这种问题。在查询的时候,将收集到的查询条件赋予一个对象的属性,当然,这个对象的类型就是需要查询的实体对象。例如,在 NHibernate 中存在一个 User 的类型,我们需要对它的姓名和口令进行组合查询,User 的定义如下:namespace Demo.Dao.Domain{ // 用户对象 public c...
阅读全文
摘要: 在这个项目中,我们将允许用户在没有注册登录的情况下将专辑加入购物车,但是,在完成结账的时候必须完成注册工作。购物和结账将会被分离到两个控制器中:一个 ShoppingCart 控制器,允许匿名用户使用购物车,另一个 Checkout 控制器处理结账。我们先从购物车的控制器开始,然后在下一部分来处理结帐。加入购物车,订单和订单明细的模型类在购物车和结账的处理中将会使用到一些新的类,在 Models 文件夹上右键,然后使用下面的代码增加一个新的类 Cart.using System.ComponentModel.DataAnnotations;namespace MvcMusicStore.Mod
阅读全文