# 跟我一起學WCF(8)——WCF中Session、實例管理詳解
## 一、引言
由前面幾篇博文我們知道,WCF是微軟基于SOA建立的一套在分布式環境中各個相對獨立的應用進行交流(Communication)的框架,它實現了最新的基于WS-*規范。按照SOA的原則,相對獨自的業務邏輯以Service的形式進行封裝,調用者通過消息(Messaging)的方式來調用服務。對于承載某個業務功能實現的服務應該具有上下文(Context)無關性,意思就是說構造服務的操作(Operation)不應該綁定到具體的調用上下文,對于任何的調用,具有什么的樣輸入就會對應怎樣的輸出。因為SOA一個最大的目標是盡可能地實現重用,只有具有Context無關性,服務才能最大限度的重用。即從軟件架構角度理解為,一個模塊只有盡可能的獨立,即具有上下文無關性,才能被最大限度的重用。軟件體系一直在強調低耦合也是這個道理。
但是在某些場景下,我們卻希望系統為我們創建一個Session來保留Client和Service的交互的狀態,如Asp.net中Session的機制一樣,WCF也提供了對Session的支持。下面就具體看看WCF中對Session的實現。
## 二、WCF中Session詳細介紹
## 2.1 Asp.net的Session與WCF中的Session
在WCF中,Session屬于Service Contract的范疇,并在Service Contract定義中通過[SessionModel](http://msdn.microsoft.com/zh-cn/library/system.servicemodel.servicecontractattribute.sessionmode(v=vs.90).aspx)參數來實現。WCF中會話具有以下幾個重要的特征:
* Session都是由Client端顯示啟動和終止的。
在WCF中Client通過創建的代理對象來和服務進行交互,在支持Session的默認情況下,Session是和具體的代理對象綁定在一起,當Client通過調用代理對象的某個方法來訪問服務時,Session就被初始化,直到代理的關閉,Session則被終止。我們可以通過兩種方式來關閉Proxy:一是調用[ICommunicationObject.Close 方法](http://msdn.microsoft.com/zh-cn/library/ms195520(v=vs.110).aspx),二是調用[ClientBase<TChannel>.Close 方法](http://msdn.microsoft.com/zh-cn/library/ms575273(v=vs.110).aspx) 。我們也可以通過服務中的某個操作方法來初始化、或者終止Session,可以通過OperationContractAttribute的IsInitiating和[IsTerminating](http://msdn.microsoft.com/zh-cn/library/system.servicemodel.operationcontractattribute.isterminating(v=vs.110).aspx)參數來指定初始化和終止Session的Operation。
* 在WCF會話期間,傳遞的消息按照它發送的順序被接收。
* WCF并沒有為Session的支持保存相關的狀態數據。
講到Session,做過Asp.net開發的人,自然想到的就是Asp.net中的Session。它們只是名字一樣,在實現機制上有很大的不同。Asp.net中的Session具有以下特性:
* Asp.net的Session總是由服務端啟動的,即在服務端進行初始化的。
* Asp.net中的Session是無需,不能保證請求處理是有序的。
* Asp.net是通過在服務端以某種方式保存State數據來實現對Session的支持,例如保存在Web Server端的內存中。
## 2.2 WCF中服務實例管理
對于Client來說,它實際上不能和Service進行直接交互,它只能通過客戶端創建的Proxy來間接地和Service進行交互,然而真正的調用而是通過服務實例來進行的。我們把通過Client的調用來創建最終的服務實例過程稱作激活,在.NET Remoting中包括Singleton模式、SingleCall模式和客戶端激活方式,WCF中也有類似的服務激活方式:單調服務(PerCall)、會話服務(PerSession)和單例服務(Singleton)。
* **單調服務(Percall)**:為每個客戶端請求分配一個新的服務實例。類似.NET Remoting中的SingleCall模式
* **會話服務(Persession)**:在會話期間,為每次客戶端請求共享一個服務實例,類似.NET Remoting中的客戶端激活模式。
* **單例服務(Singleton)**:所有客戶端請求都共享一個相同的服務實例,類似于.NET Remoting的Singleton模式。但它的激活方式需要注意一點:當為對于的服務類型進行Host的時候,與之對應的服務實例就被創建出來,之后所有的服務調用都由這個服務實例進行處理。
WCF中服務激活的默認方式是PerSession,但不是所有的Bingding都支持Session,比如BasicHttpBinding就不支持Session。你也可以通過下面的方式使ServiceContract不支持Session.
```
[ServiceContract(SessionMode = SessionMode.NotAllowed)]
```
下面分別介紹下這三種激活方式的實現。
## 三、WCF中實例管理的實現
WCF中服務激活的默認是PerSession的方式,下面就看看PerSession的實現方式。我們還是按照前面幾篇博文的方式來實現使用PerSession方式的WCF服務程序。
第一步:自然是實現我們的WCF契約和契約的服務實現。具體的實現代碼如下所示:
```
1 // 服務契約的定義
2 [ServiceContract]
3 public interface ICalculator
4 {
5 [OperationContract(IsOneWay = true)]
6 void Increase();
7
8 [OperationContract]
9 int GetResult();
10 }
11
12 // 契約的實現
13 public class CalculatorService : ICalculator, IDisposable
14 {
15 private int _nCount = 0;
16
17 public CalculatorService()
18 {
19 Console.WriteLine("CalulatorService object has been created");
20 }
21
22 // 為了看出服務實例的釋放情況
23 public void Dispose()
24 {
25 Console.WriteLine("CalulatorService object has been Disposed");
26 }
27
28 #region ICalulator Members
29 public void Increase()
30 {
31 // 輸出Session ID
32 Console.WriteLine("The Add method is invoked and the current session ID is: {0}", OperationContext.Current.SessionId);
33 this._nCount++;
34 }
35
36 public int GetResult()
37 {
38 Console.WriteLine("The GetResult method is invoked and the current session ID is: {0}", OperationContext.Current.SessionId);
39 return this._nCount;
40 }
41 #endregion
42 }
```
為了讓大家對服務對象的創建和釋放有一個直觀的認識,我特意對服務類實現了構造函數和IDisposable接口,同時在每個操作中輸出當前的Session ID。
第二步:實現服務宿主程序。這里還是采用控制臺程序作為服務宿主程序,具體的實現代碼如下所示:
```
1 // 服務宿主程序
2 class Program
3 {
4 static void Main(string[] args)
5 {
6 using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
7 {
8 host.Opened += delegate
9 {
10 Console.WriteLine("The Calculator Service has been started, begun to listen request...");
11 };
12
13 host.Open();
14 Console.ReadLine();
15 }
16 }
17 }
```
對應的配置文件為:
```
<!--服務宿主的配置文件-->
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name ="CalculatorBehavior">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="WCFContractAndService.CalculatorService" behaviorConfiguration="CalculatorBehavior">
<endpoint address="" binding="basicHttpBinding" contract="WCFContractAndService.ICalculator"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost:9003/CalculatorPerSession"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
```
第三步:實現完了服務宿主程序,接下來自然是實現客戶端程序來訪問服務操作。這里的客戶端也是控制臺程序,具體的實現代碼如下所示:
```
1 // 客戶端程序實現
2 class Program
3 {
4 static void Main(string[] args)
5 {
6 // Use ChannelFactory<ICalculator> to create WCF Service proxy
7 ChannelFactory<ICalculator> calculatorChannelFactory = new ChannelFactory<ICalculator>("HttpEndPoint");
8 Console.WriteLine("Create a calculator proxy :proxy1");
9 ICalculator proxy1 = calculatorChannelFactory.CreateChannel();
10 Console.WriteLine("Invoke proxy1.Increate() method");
11 proxy1.Increase();
12 Console.WriteLine("Invoke proxy1.Increate() method again");
13 proxy1.Increase();
14 Console.WriteLine("The result return via proxy1.GetResult() is: {0}", proxy1.GetResult());
15
16 Console.WriteLine("Create another calculator proxy: proxy2");
17 ICalculator proxy2 = calculatorChannelFactory.CreateChannel();
18 Console.WriteLine("Invoke proxy2.Increate() method");
19 proxy2.Increase();
20 Console.WriteLine("Invoke proxy2.Increate() method again");
21 proxy2.Increase();
22 Console.WriteLine("The result return via proxy2.GetResult() is: {0}", proxy2.GetResult());
23
24 Console.ReadLine();
25 }
26 }
```
客戶端對應的配置文件內容如下所示:
```
<!--客戶端配置文件-->
<configuration>
<system.serviceModel>
<client>
<endpoint address="http://localhost:9003/CalculatorPerSession"
binding="basicHttpBinding"
contract="WCFContractAndService.ICalculator" name="HttpEndPoint"/>
</client>
</system.serviceModel>
</configuration>
```
經過上面三步,我們就完成了PerSession方式的WCF程序了,下面看看該程序的運行結果。
首先,以管理員權限運行服務寄宿程序,運行成功后,你將看到如下圖所示的畫面:

接下來,運行客戶端對服務操作進行調用,運行成功后,你將看到服務宿主的輸出和客戶端的輸出情況如下圖所示:

從客戶端的運行結果可以看出,雖然我們兩次調用了Increase方法來增加_nCount的值,但是最終的運行結果仍然是0。這樣的運行結果好像與我們之前所說的WCF默認Session支持矛盾,因為如果WCF默認的方式PerSession的話,則服務實例是和Proxy綁定在一起,當Proxy調用任何一個操作的時候Session開始,從此Session將會與Proxy具有一樣的生命周期。按照這個描述,客戶端運行的結果應該是2而不是0。這里,我只能說運行結果并沒有錯,因為有圖有真相嘛,那到底是什么原因導致客戶端獲得_nCount值是0呢?其實在前面已經講到過,并不是所有的綁定都是支持Session的,上面程序的實現我們使用的basicHttpBinding,而basicHttpBinding是不支持Session方式的,所以WCF會采用PerCall的方式創建Service Instance,所以在服務端中對于每一個Proxy都有3個對象被創建,兩個是對Increase方法的調用會導致服務實例的激活,另一個是對GetResult方法的調用導致服務實例的激活。因為是PerCall方式,所以每次調用完之后,就會對服務實例進行釋放,所以對應的就有3行服務對象釋放輸出。并且由于使用的是不支持Session的binding,所以Session ID的輸出也為null。所以,上面WCF程序其實是PerCall方式的實現。
既然,上面的運行結果是由于使用了不支持Session的basicHttpBinding導致的,下面看看使用一個支持Session的Binding:wsHttpBinding來看看運行結果是怎樣的,這里的修改很簡單,只需要把宿主和客戶端的配置文件把綁定類型修改為wsHttpBinding就可以了。
```
<!--客戶端配置文件-->
<configuration>
<system.serviceModel>
<client>
<endpoint address="http://localhost:9003/CalculatorPerSession"
binding="**wsHttpBinding**"
contract="WCFContractAndService.ICalculator" name="HttpEndPoint"/>
</client>
</system.serviceModel>
</configuration>
<!--服務宿主的配置文件-->
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name ="CalculatorBehavior">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="WCFContractAndService.CalculatorService" behaviorConfiguration="CalculatorBehavior">
<endpoint address="" binding="**wsHttpBinding**" contract="WCFContractAndService.ICalculator"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost:9003/CalculatorPerSession"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
```
現在我們再運行下上面的程序來看看此時的執行結果,具體的運行結果如下圖所示:

從上面的運行結果可以看出,此時兩個Proxy的運行結果都是2,可以看出此時服務激活方式采用的是PerSession方式。此時對于服務端就只有兩個服務實例被創建了,并且對于每個服務實例具有相同的Session ID。 另外由于Client的Proxy還依然存在,服務實例也不會被回收掉,從上面服務端運行的結果也可以證實這點,因為運行結果中沒有對象唄Disposable的輸出。你可以在客戶端顯式調用ICommunicationObject.Close方法來顯式關閉掉Proxy,在客戶端添加對Proxy的顯示關閉代碼,此時客戶端的代碼修改為如下所示:
```
1 // 客戶端程序實現
2 class Program
3 {
4 static void Main(string[] args)
5 {
6 // Use ChannelFactory<ICalculator> to create WCF Service proxy
7 ChannelFactory<ICalculator> calculatorChannelFactory = new ChannelFactory<ICalculator>("HttpEndPoint");
8 Console.WriteLine("Create a calculator proxy :proxy1");
9 ICalculator proxy1 = calculatorChannelFactory.CreateChannel();
10 Console.WriteLine("Invoke proxy1.Increate() method");
11 proxy1.Increase();
12 Console.WriteLine("Invoke proxy1.Increate() method again");
13 proxy1.Increase();
14 Console.WriteLine("The result return via proxy1.GetResult() is: {0}", proxy1.GetResult());
15 **(proxy1 as ICommunicationObject).Close(); // 顯示關閉Proxy**
16
17 Console.WriteLine("Create another calculator proxy: proxy2");
18 ICalculator proxy2 = calculatorChannelFactory.CreateChannel();
19 Console.WriteLine("Invoke proxy2.Increate() method");
20 proxy2.Increase();
21 Console.WriteLine("Invoke proxy2.Increate() method again");
22 proxy2.Increase();
23 Console.WriteLine("The result return via proxy2.GetResult() is: {0}", proxy2.GetResult());
24 **(proxy2 as ICommunicationObject).Close();** 25
26 Console.ReadLine();
27 }
28 }
```
此時,服務對象的Dispose()方法將會調用,此時服務端的運行結果如下圖所示:

上面演示了默認支持Session的情況,下面我們修改服務契約使之不支持Session,此時只需要知道ServiceContract的SessionMode為NotAllowed即可。
```
[ServiceContract**(SessionMode= SessionMode.NotAllowed)**] // 是服務契約不支持Session
public interface ICalculator
{
[OperationContract(IsOneWay = true)]
void Increase();
[OperationContract]
int GetResult();
}
```
此時,由于服務契約不支持Session,此時服務激活方式采用的仍然是PerCall。運行結果與前面采用不支持Session的綁定的運行結果一樣,這里就不一一貼圖了。
除了通過顯式修改ServiceContract的SessionMode來使服務契約支持或不支持Session外,還可以定制操作對Session的支持。定制操作對Session的支持可以通過OperationContract的IsInitiating和InTerminating屬性設置。
```
1 // 服務契約的定義
2 [ServiceContract(SessionMode= SessionMode.Required)] // 顯式使服務契約支持Session
3 public interface ICalculator
4 {
5 // IsInitiating:該值指示方法是否實現可在服務器上啟動會話(如果存在會話)的操作,默認值是true
6 // IsTerminating:獲取或設置一個值,該值指示服務操作在發送答復消息(如果存在)后,是否會導致服務器關閉會話,默認值是false
7 [OperationContract(IsOneWay = true, IsInitiating =true, IsTerminating=false )]
8 void Increase();
9
10 [OperationContract(IsInitiating = true, IsTerminating = true)]
11 int GetResult();
12 }
```
在上面代碼中,對兩個操作都設置InInitiating的屬性為true,意味著調用這兩個操作都會啟動會話,而把GetResult操作的IsTerminating設置為true,意味著調用完這個操作后,會導致服務關閉掉會話,因為在Session方式下,Proxy與Session有一致的生命周期,所以關閉Session也就是關閉proxy對象,所以如果后面再對proxy對象的任何一個方法進行調用將會導致異常,下面代碼即演示了這種情況。
```
1 // 客戶端程序實現
2 class Program
3 {
4 static void Main(string[] args)
5 {
6 // Use ChannelFactory<ICalculator> to create WCF Service proxy
7 ChannelFactory<ICalculator> calculatorChannelFactory = new ChannelFactory<ICalculator>("HttpEndPoint");
8 Console.WriteLine("Create a calculator proxy :proxy1");
9 ICalculator proxy1 = calculatorChannelFactory.CreateChannel();
10 Console.WriteLine("Invoke proxy1.Increate() method");
11 proxy1.Increase();
12 Console.WriteLine("Invoke proxy1.Increate() method again");
13 proxy1.Increase();
14 Console.WriteLine("The result return via proxy1.GetResult() is: {0}", proxy1.GetResult());
15 **try
16 {
17 proxy1.Increase(); // session關閉后對proxy1.Increase方法調用將會導致異常
18 }
19 catch (Exception ex) // 異常捕獲
20 {
21 Console.WriteLine("在Session關閉后調用Increase方法失敗,錯誤信息為:{0}", ex.Message);
22 }** 23
24 Console.WriteLine("Create another calculator proxy: proxy2");
25 ICalculator proxy2 = calculatorChannelFactory.CreateChannel();
26 Console.WriteLine("Invoke proxy2.Increate() method");
27 proxy2.Increase();
28 Console.WriteLine("Invoke proxy2.Increate() method again");
29 proxy2.Increase();
30 Console.WriteLine("The result return via proxy2.GetResult() is: {0}", proxy2.GetResult());
31
32 Console.ReadLine();
33 }
34 }
```
此時運行結果也驗證我們上面的分析,客戶端和服務端的運行結果如下圖所示:

上面演示了PerSession和PerCall的兩種服務對象激活方式,下面看看Single的激活方式運行的結果。首先通過ServiceBehavior的InstanceContextMode屬性顯式指定激活方式為Single,由于[ServiceBehaviorAttribute](http://msdn.microsoft.com/zh-cn/library/System.ServiceModel.ServiceBehaviorAttribute(v=vs.110).aspx)特性只能應用于類上,所以把該特性應用于CalculatorService類上,此時服務實現的代碼如下所示:
```
// 契約的實現
// ServiceBehavior屬性只能應用在類上
**[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] // 顯示指定PerSingle方式**
public class CalculatorService : ICalculator, IDisposable
{
private int _nCount = 0;
public CalculatorService()
{
Console.WriteLine("CalulatorService object has been created");
}
// 為了看出服務實例的釋放情況
public void Dispose()
{
Console.WriteLine("CalulatorService object has been Disposed");
}
#region ICalulator Members
public void Increase()
{
// 輸出Session ID
Console.WriteLine("The Add method is invoked and the current session ID is: {0}", OperationContext.Current.SessionId);
this._nCount++;
}
public int GetResult()
{
Console.WriteLine("The GetResult method is invoked and the current session ID is: {0}", OperationContext.Current.SessionId);
return this._nCount;
}
#endregion
}
```
此時運行服務宿主的輸出結果如下圖所示:

從運行結果可以看出,對于Single方式,服務實例在服務類型被寄宿的時候就已經創建了,對于PerCall和PerSession方式而是在通過Proxy調用相應的服務操作之后,服務實例才開始創建的。下面運行客戶端程序,你將看到如下圖所示的運行結果:

此時,第二個Proxy返回的結果是4而不是2,這是因為采用Single方式只存在一個服務實例,所有的調用狀態都將保留,所以_nCount的值在原來的基礎上繼續累加。
## 四、總結
到這里,本文的分享就結束了,本文主要分享了WCF中實例管理的實現。從WCF的實例實現可以看出,WCF實例實現是借鑒了.NET Remoting中實例實現,然后分別分享了服務實例三種激活方式在WCF中的實現,并通過對運行結果進行對比來讓大家理解它們之間的區別。
本文所以源碼:[WCFInstanceManager.zip](http://files.cnblogs.com/zhili/WCFInstanceManager.zip)
- C# 基礎知識系列
- C# 基礎知識系列 專題一:深入解析委托——C#中為什么要引入委托
- C# 基礎知識系列 專題二:委托的本質論
- C# 基礎知識系列 專題三:如何用委托包裝多個方法——委托鏈
- C# 基礎知識系列 專題四:事件揭秘
- C# 基礎知識系列 專題五:當點擊按鈕時觸發Click事件背后發生的事情
- C# 基礎知識系列 專題六:泛型基礎篇——為什么引入泛型
- C# 基礎知識系列 專題七: 泛型深入理解(一)
- C# 基礎知識系列 專題八: 深入理解泛型(二)
- C# 基礎知識系列 專題九: 深入理解泛型可變性
- C#基礎知識系列 專題十:全面解析可空類型
- C# 基礎知識系列 專題十一:匿名方法解析
- C#基礎知識系列 專題十二:迭代器
- C#基礎知識 專題十三:全面解析對象集合初始化器、匿名類型和隱式類型
- C# 基礎知識系列 專題十四:深入理解Lambda表達式
- C# 基礎知識系列 專題十五:全面解析擴展方法
- C# 基礎知識系列 專題十六:Linq介紹
- C#基礎知識系列 專題十七:深入理解動態類型
- 你必須知道的異步編程 C# 5.0 新特性——Async和Await使異步編程更簡單
- 全面解析C#中參數傳遞
- C#基礎知識系列 全面解析C#中靜態與非靜態
- C# 基礎知識系列 C#中易混淆的知識點
- C#進階系列
- C#進階系列 專題一:深入解析深拷貝和淺拷貝
- C#進階系列 專題二:你知道Dictionary查找速度為什么快嗎?
- C# 開發技巧系列
- C# 開發技巧系列 使用C#操作Word和Excel程序
- C# 開發技巧系列 使用C#操作幻燈片
- C# 開發技巧系列 如何動態設置屏幕分辨率
- C# 開發技巧系列 C#如何實現圖片查看器
- C# 開發技巧 如何防止程序多次運行
- C# 開發技巧 實現屬于自己的截圖工具
- C# 開發技巧 如何使不符合要求的元素等于離它最近的一個元素
- C# 線程處理系列
- C# 線程處理系列 專題一:線程基礎
- C# 線程處理系列 專題二:線程池中的工作者線程
- C# 線程處理系列 專題三:線程池中的I/O線程
- C# 線程處理系列 專題四:線程同步
- C# 線程處理系列 專題五:線程同步——事件構造
- C# 線程處理系列 專題六:線程同步——信號量和互斥體
- C# 多線程處理系列專題七——對多線程的補充
- C#網絡編程系列
- C# 網絡編程系列 專題一:網絡協議簡介
- C# 網絡編程系列 專題二:HTTP協議詳解
- C# 網絡編程系列 專題三:自定義Web服務器
- C# 網絡編程系列 專題四:自定義Web瀏覽器
- C# 網絡編程系列 專題五:TCP編程
- C# 網絡編程系列 專題六:UDP編程
- C# 網絡編程系列 專題七:UDP編程補充——UDP廣播程序的實現
- C# 網絡編程系列 專題八:P2P編程
- C# 網絡編程系列 專題九:實現類似QQ的即時通信程序
- C# 網絡編程系列 專題十:實現簡單的郵件收發器
- C# 網絡編程系列 專題十一:實現一個基于FTP協議的程序——文件上傳下載器
- C# 網絡編程系列 專題十二:實現一個簡單的FTP服務器
- C# 互操作性入門系列
- C# 互操作性入門系列(一):C#中互操作性介紹
- C# 互操作性入門系列(二):使用平臺調用調用Win32 函數
- C# 互操作性入門系列(三):平臺調用中的數據封送處理
- C# 互操作性入門系列(四):在C# 中調用COM組件
- CLR
- 談談: String 和StringBuilder區別和選擇
- 談談:程序集加載和反射
- 利用反射獲得委托和事件以及創建委托實例和添加事件處理程序
- 談談:.Net中的序列化和反序列化
- C#設計模式
- UML類圖符號 各種關系說明以及舉例
- C#設計模式(1)——單例模式
- C#設計模式(2)——簡單工廠模式
- C#設計模式(3)——工廠方法模式
- C#設計模式(4)——抽象工廠模式
- C#設計模式(5)——建造者模式(Builder Pattern)
- C#設計模式(6)——原型模式(Prototype Pattern)
- C#設計模式(7)——適配器模式(Adapter Pattern)
- C#設計模式(8)——橋接模式(Bridge Pattern)
- C#設計模式(9)——裝飾者模式(Decorator Pattern)
- C#設計模式(10)——組合模式(Composite Pattern)
- C#設計模式(11)——外觀模式(Facade Pattern)
- C#設計模式(12)——享元模式(Flyweight Pattern)
- C#設計模式(13)——代理模式(Proxy Pattern)
- C#設計模式(14)——模板方法模式(Template Method)
- C#設計模式(15)——命令模式(Command Pattern)
- C#設計模式(16)——迭代器模式(Iterator Pattern)
- C#設計模式(17)——觀察者模式(Observer Pattern)
- C#設計模式(18)——中介者模式(Mediator Pattern)
- C#設計模式(19)——狀態者模式(State Pattern)
- C#設計模式(20)——策略者模式(Stragety Pattern)
- C#設計模式(21)——責任鏈模式
- C#設計模式(22)——訪問者模式(Vistor Pattern)
- C#設計模式(23)——備忘錄模式(Memento Pattern)
- C#設計模式總結
- WPF快速入門系列
- WPF快速入門系列(1)——WPF布局概覽
- WPF快速入門系列(2)——深入解析依賴屬性
- WPF快速入門系列(3)——深入解析WPF事件機制
- WPF快速入門系列(4)——深入解析WPF綁定
- WPF快速入門系列(5)——深入解析WPF命令
- WPF快速入門系列(6)——WPF資源和樣式
- WPF快速入門系列(7)——深入解析WPF模板
- WPF快速入門系列(8)——MVVM快速入門
- WPF快速入門系列(9)——WPF任務管理工具實現
- ASP.NET 開發
- ASP.NET 開發必備知識點(1):如何讓Asp.net網站運行在自定義的Web服務器上
- ASP.NET 開發必備知識點(2):那些年追過的ASP.NET權限管理
- ASP.NET中實現回調
- 跟我一起學WCF
- 跟我一起學WCF(1)——MSMQ消息隊列
- 跟我一起學WCF(2)——利用.NET Remoting技術開發分布式應用
- 跟我一起學WCF(3)——利用Web Services開發分布式應用
- 跟我一起學WCF(3)——利用Web Services開發分布式應用
- 跟我一起學WCF(4)——第一個WCF程序
- 跟我一起學WCF(5)——深入解析服務契約 上篇
- 跟我一起學WCF(6)——深入解析服務契約 下篇
- 跟我一起學WCF(7)——WCF數據契約與序列化詳解
- 跟我一起學WCF(8)——WCF中Session、實例管理詳解
- 跟我一起學WCF(9)——WCF回調操作的實現
- 跟我一起學WCF(10)——WCF中事務處理
- 跟我一起學WCF(11)——WCF中隊列服務詳解
- 跟我一起學WCF(12)——WCF中Rest服務入門
- 跟我一起學WCF(13)——WCF系列總結
- .NET領域驅動設計實戰系列
- .NET領域驅動設計實戰系列 專題一:前期準備之EF CodeFirst
- .NET領域驅動設計實戰系列 專題二:結合領域驅動設計的面向服務架構來搭建網上書店
- .NET領域驅動設計實戰系列 專題三:前期準備之規約模式(Specification Pattern)
- .NET領域驅動設計實戰系列 專題四:前期準備之工作單元模式(Unit Of Work)
- .NET領域驅動設計實戰系列 專題五:網上書店規約模式、工作單元模式的引入以及購物車的實現
- .NET領域驅動設計實戰系列 專題六:DDD實踐案例:網上書店訂單功能的實現
- .NET領域驅動設計實戰系列 專題七:DDD實踐案例:引入事件驅動與中間件機制來實現后臺管理功能
- .NET領域驅動設計實戰系列 專題八:DDD案例:網上書店分布式消息隊列和分布式緩存的實現
- .NET領域驅動設計實戰系列 專題九:DDD案例:網上書店AOP和站點地圖的實現
- .NET領域驅動設計實戰系列 專題十:DDD擴展內容:全面剖析CQRS模式實現
- .NET領域驅動設計實戰系列 專題十一:.NET 領域驅動設計實戰系列總結