# [.NET領域驅動設計實戰系列]專題九:DDD案例:網上書店AOP和站點地圖的實現
## 一、引言
在前面一專題介紹到,要讓緩存生效還需要實現對AOP(面向切面編程)的支持。所以本專題將介紹了網上書店案例中AOP的實現。關于AOP的概念,大家可以參考文章:[http://www.cnblogs.com/jin-yuan/p/3811077.html](http://www.cnblogs.com/jin-yuan/p/3811077.html)。這里我簡單介紹下AOP:AOP可以理解為對方法進行截獲,這樣就可以在方法調用前或調用后插入需要的邏輯。例如可以在方法調用前,加入緩存查找邏輯等。這里緩存查找邏輯就在方法調用前被執行。通過對AOP的支持,每個方法就可以分為3部分了,方法調用前邏輯->具體需要調用的方法->方法調用后的邏輯。也就是在方法調用的時候“切了一刀”。
## 二、網上書店AOP的實現
你可以從零開始去實現AOP,但是目前已經存在很多AOP框架了,所以在本案例中將直接通過Unity的AOP框架(Unity.Interception)來實現網上書店對AOP的支持。通常AOP的實現放在基礎設施層進行實現,因為可能其他所有層都需要加入對AOP的支持。本案例中將對兩個方面的AOP進行實現,一個是方法調用前緩存的記錄或查找,另一個是方法調用后異常信息的記錄。在實現具體代碼之前,我們需要在基礎設施層通過Nuget來引入Unity.Interception包。添加成功之后,我們需要定義兩個類分別去實現AOP框架中**IInterceptionBehavior**接口。由于本案例中需要對緩存和異常日志功能進行AOP實現,自然就需要定義CachingBehavior和ExceptionLoggingBehavior兩個類去實現**IInterceptionBehavior**接口。首先讓我們看看CachingBehavior類的實現,具體實現代碼如下所示:
```
// 緩存AOP的實現
public class CachingBehavior : IInterceptionBehavior
{
private readonly ICacheProvider _cacheProvider;
public CachingBehavior()
{
_cacheProvider = ServiceLocator.Instance.GetService<ICacheProvider>();
}
// 生成緩存值的鍵值
private string GetValueKey(CacheAttribute cachingAttribute, IMethodInvocation input)
{
switch (cachingAttribute.Method)
{
// 如果是Remove,則不存在特定值鍵名,所有的以該方法名稱相關的緩存都需要清除
case CachingMethod.Remove:
return null;
// 如果是Get或者Update,則需要產生一個針對特定參數值的鍵名
case CachingMethod.Get:
case CachingMethod.Update:
if (input.Arguments != null &&
input.Arguments.Count > 0)
{
var sb = new StringBuilder();
for (var i = 0; i < input.Arguments.Count; i++)
{
sb.Append(input.Arguments[i]);
if (i != input.Arguments.Count - 1)
sb.Append("_");
}
return sb.ToString();
}
else
return "NULL";
default:
throw new InvalidOperationException("無效的緩存方式。");
}
}
#region IInterceptionBehavior Members
public IEnumerable<Type> GetRequiredInterfaces()
{
return Type.EmptyTypes;
}
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
// 獲得被攔截的方法
var method = input.MethodBase;
var key = method.Name; // 獲得攔截的方法名
// 如果攔截的方法定義了Cache屬性,說明需要對該方法的結果需要進行緩存
if (!method.IsDefined(typeof (CacheAttribute), false))
return getNext().Invoke(input, getNext);
var cachingAttribute = (CacheAttribute)method.GetCustomAttributes(typeof (CacheAttribute), false)[0];
var valueKey = GetValueKey(cachingAttribute, input);
switch (cachingAttribute.Method)
{
case CachingMethod.Get:
try
{
// 如果緩存中存在該鍵值的緩存,則直接返回緩存中的結果退出
if (_cacheProvider.Exists(key, valueKey))
{
var value = _cacheProvider.Get(key, valueKey);
var arguments = new object[input.Arguments.Count];
input.Arguments.CopyTo(arguments, 0);
return new VirtualMethodReturn(input, value, arguments);
}
else // 否則先調用方法,再把返回結果進行緩存
{
var methodReturn = getNext().Invoke(input, getNext);
_cacheProvider.Add(key, valueKey, methodReturn.ReturnValue);
return methodReturn;
}
}
catch (Exception ex)
{
return new VirtualMethodReturn(input, ex);
}
case CachingMethod.Update:
try
{
var methodReturn = getNext().Invoke(input, getNext);
if (_cacheProvider.Exists(key))
{
if (cachingAttribute.IsForce)
{
_cacheProvider.Remove(key);
_cacheProvider.Add(key, valueKey, methodReturn.ReturnValue);
}
else
_cacheProvider.Update(key, valueKey, methodReturn);
}
else
_cacheProvider.Add(key, valueKey, methodReturn.ReturnValue);
return methodReturn;
}
catch (Exception ex)
{
return new VirtualMethodReturn(input, ex);
}
case CachingMethod.Remove:
try
{
var removeKeys = cachingAttribute.CorrespondingMethodNames;
foreach (var removeKey in removeKeys)
{
if (_cacheProvider.Exists(removeKey))
_cacheProvider.Remove(removeKey);
}
**// 執行具體截獲的方法** var methodReturn = getNext().Invoke(input, getNext);
return methodReturn;
}
catch (Exception ex)
{
return new VirtualMethodReturn(input, ex);
}
default: break;
}
return getNext().Invoke(input, getNext);
}
public bool WillExecute
{
get { return true; }
}
#endregion
}
```
從上面代碼可以看出,通過Unity.Interception框架來實現AOP變得非常簡單了,我們只需要實現IInterceptionBehavior接口中的Invoke方法和WillExecute屬性即可。并且從上面代碼可以看出,AOP的支持最核心代碼實現在于Invoke方法的實現。既然我們需要在方法調用前查找緩存,如果緩存不存在再調用方法從數據庫中進行查找,如果存在則直接從緩存中進行讀取數據即可。自然需要在 getNext().Invoke(input, getNext)代碼執行前進緩存進行查找,然而上面CachingBehavior類正式這樣實現的。
介紹完緩存功能AOP的實現之后,下面具體看看異常日志的AOP實現。具體實現代碼如下所示:
```
// 用于異常日志記錄的攔截行為
public class ExceptionLoggingBehavior :IInterceptionBehavior
{
/// <summary>
/// 需要攔截的對象類型的接口
/// </summary>
/// <returns></returns>
public IEnumerable<Type> GetRequiredInterfaces()
{
return Type.EmptyTypes;
}
/// <summary>
/// 通過該方法來攔截調用并執行所需要的攔截行為
/// </summary>
/// <param name="input">調用攔截目標時的輸入信息</param>
/// <param name="getNext">通過行為鏈來獲取下一個攔截行為的委托</param>
/// <returns>從攔截目標獲得的返回信息</returns>
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
// 執行目標方法
var methodReturn = getNext().Invoke(input, getNext);
// 方法執行后的處理
if (methodReturn.Exception != null)
{
Utils.Log(methodReturn.Exception);
}
return methodReturn;
}
// 表示當攔截行為被調用時,是否需要執行某些操作
public bool WillExecute
{
get { return true; }
}
}
```
異常日志功能的AOP實現與緩存功能的AOP實現類似,只是一個需要在方法執行前注入,而一個是在方法執行后進行注入罷了,其實現原理都是在截獲的方法前后進行。方法截獲功能AOP框架已經幫我們實現了。
到此,我們網上書店AOP的實現就已經完成了,但要正式生效還需要通過配置文件把AOP的實現注入到需要截獲的方法當中去,這樣執行這些方法才會執行注入的行為。對應的配置文件如下標紅部分所示:
```
<!--Unity的配置信息-->
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" />
<container>
<extension type="Interception" />
<!--Cache Provider-->
<register type="OnlineStore.Infrastructure.Caching.ICacheProvider, OnlineStore.Infrastructure" mapTo="OnlineStore.Infrastructure.Caching.EntLibCacheProvider, OnlineStore.Infrastructure" />
<!--倉儲接口的注冊-->
<register type="OnlineStore.Domain.Repositories.IRepositoryContext, OnlineStore.Domain" mapTo="OnlineStore.Repositories.EntityFramework.EntityFrameworkRepositoryContext, OnlineStore.Repositories">
<lifetime type="singleton" />
</register>
<register type="OnlineStore.Domain.Repositories.IProductRepository, OnlineStore.Domain" mapTo="OnlineStore.Repositories.EntityFramework.ProductRepository, OnlineStore.Repositories" />
<register type="OnlineStore.Domain.Repositories.ICategoryRepository, OnlineStore.Domain" mapTo="OnlineStore.Repositories.EntityFramework.CategoryRepository, OnlineStore.Repositories" />
<register type="OnlineStore.Domain.Repositories.IProductCategorizationRepository, OnlineStore.Domain" mapTo="OnlineStore.Repositories.EntityFramework.ProductCategorizationRepository, OnlineStore.Repositories" />
<register type="OnlineStore.Domain.Repositories.IUserRepository, OnlineStore.Domain" mapTo="OnlineStore.Repositories.EntityFramework.UserRepository, OnlineStore.Repositories" />
<register type="OnlineStore.Domain.Repositories.IShoppingCartRepository, OnlineStore.Domain" mapTo="OnlineStore.Repositories.EntityFramework.ShoppingCartRepository, OnlineStore.Repositories" />
<register type="OnlineStore.Domain.Repositories.IShoppingCartItemRepository, OnlineStore.Domain" mapTo="OnlineStore.Repositories.EntityFramework.ShoppingCartItemRepository, OnlineStore.Repositories" />
<register type="OnlineStore.Domain.Repositories.IOrderRepository, OnlineStore.Domain" mapTo="OnlineStore.Repositories.EntityFramework.OrderRepository, OnlineStore.Repositories" />
<register type="OnlineStore.Domain.Repositories.IUserRepository, OnlineStore.Domain" mapTo="OnlineStore.Repositories.EntityFramework.UserRepository, OnlineStore.Repositories" />
<register type="OnlineStore.Domain.Repositories.IUserRoleRepository, OnlineStore.Domain" mapTo="OnlineStore.Repositories.EntityFramework.UserRoleRepository, OnlineStore.Repositories" />
<register type="OnlineStore.Domain.Repositories.IRoleRepository, OnlineStore.Domain" mapTo="OnlineStore.Repositories.EntityFramework.RoleRepository, OnlineStore.Repositories" />
<!--Domain Services-->
<register type="OnlineStore.Domain.Services.IDomainService, OnlineStore.Domain" mapTo="OnlineStore.Domain.Services.DomainService, OnlineStore.Domain" />
** <!--應用服務的注冊-->
<register type="OnlineStore.ServiceContracts.IProductService, OnlineStore.ServiceContracts" mapTo="OnlineStore.Application.ServiceImplementations.ProductServiceImp, OnlineStore.Application">
<!--注入AOP功能的實現-->
<interceptor type="InterfaceInterceptor" />
<interceptionBehavior type="OnlineStore.Infrastructure.InterceptionBehaviors.CachingBehavior, OnlineStore.Infrastructure" />
<interceptionBehavior type="OnlineStore.Infrastructure.InterceptionBehaviors.ExceptionLoggingBehavior, OnlineStore.Infrastructure" />
</register>
<register type="OnlineStore.ServiceContracts.IOrderService, OnlineStore.ServiceContracts" mapTo="OnlineStore.Application.ServiceImplementations.OrderServiceImp, OnlineStore.Application">
<!--注入AOP功能的實現-->
<interceptor type="InterfaceInterceptor" />
<interceptionBehavior type="OnlineStore.Infrastructure.InterceptionBehaviors.CachingBehavior, OnlineStore.Infrastructure" />
<interceptionBehavior type="OnlineStore.Infrastructure.InterceptionBehaviors.ExceptionLoggingBehavior, OnlineStore.Infrastructure" />
</register>
<register type="OnlineStore.ServiceContracts.IUserService, OnlineStore.ServiceContracts" mapTo="OnlineStore.Application.ServiceImplementations.UserServiceImp, OnlineStore.Application">
<!--注入AOP功能的實現-->
<interceptor type="InterfaceInterceptor" />
<interceptionBehavior type="OnlineStore.Infrastructure.InterceptionBehaviors.CachingBehavior, OnlineStore.Infrastructure" />
<interceptionBehavior type="OnlineStore.Infrastructure.InterceptionBehaviors.ExceptionLoggingBehavior, OnlineStore.Infrastructure" />
</register>**
<!--Domain Event Handlers-->
<register type="OnlineStore.Domain.Events.IDomainEventHandler`1[[OnlineStore.Domain.Events.OrderDispatchedEvent, OnlineStore.Domain]], OnlineStore.Domain" mapTo="OnlineStore.Domain.Events.EventHandlers.OrderDispatchedEventHandler, OnlineStore.Domain" name="OrderDispatchedEventHandler" />
<register type="OnlineStore.Domain.Events.IDomainEventHandler`1[[OnlineStore.Domain.Events.OrderConfirmedEvent, OnlineStore.Domain]], OnlineStore.Domain" mapTo="OnlineStore.Domain.Events.EventHandlers.OrderConfirmedEventHandler, OnlineStore.Domain" name="OrderConfirmedEventHandler" />
<!--Event Handlers-->
<register name="orderSendEmailHandler" type="OnlineStore.Events.IEventHandler`1[[OnlineStore.Domain.Events.OrderDispatchedEvent, OnlineStore.Domain]], OnlineStore.Events" mapTo="OnlineStore.Events.Handlers.SendEmailHandler, OnlineStore.Events.Handlers" />
<!--Event Aggregator-->
<register type="OnlineStore.Events.IEventAggregator, OnlineStore.Events" mapTo="OnlineStore.Events.EventAggregator, OnlineStore.Events">
<constructor>
<param name="handlers">
<array>
<dependency name="orderSendEmailHandler" type="OnlineStore.Events.IEventHandler`1[[OnlineStore.Domain.Events.OrderDispatchedEvent, OnlineStore.Domain]], OnlineStore.Events" />
</array>
</param>
</constructor>
</register>
<!--Event Bus-->
<!--<register type="OnlineStore.Events.Bus.IEventBus, OnlineStore.Events" mapTo="OnlineStore.Events.Bus.EventBus, OnlineStore.Events">
<lifetime type="singleton" />
</register>-->
<!--注入MsmqEventBus-->
<register type="OnlineStore.Events.Bus.IEventBus, OnlineStore.Events"
mapTo="OnlineStore.Events.Bus.MsmqEventBus, OnlineStore.Events">
<lifetime type="singleton" />
<constructor>
<param name="path" value=".\Private$\OnlineStoreQueue" />
</constructor>
</register>
</container>
</unity>
<!--END: Unity-->
```
到此,網上書店案例中AOP的實現就完成了。通過上面的配置可以看出,客戶端在調用應用服務方法前后會調用我們注入的行為,即緩存行為和異常日志行為。通過對AOP功能的支持,就不需要為每個需要進行緩存或需要異常日志行為的方法來重復寫這些相同的邏輯了。從而避免了重復代碼的重復實現,提高了代碼的重用性和降低了模塊之間的依賴性。
## 三、網上書店案例中站點地圖的實現
在大部分網站中都實現了站點地圖的功能,在Asp.net中,我們可以通過SiteMap模塊來實現站點地圖的功能,在Asp.net MVC也可以通過MvcSiteMapProvider第三方開源框架來實現站點地圖。所以針對網上書店案例,站點地圖的支持也是必不可少的。下面讓我們具體看看站點地圖在本案例中是如何去實現的呢?
在看實現代碼之前,讓我們先來理清下實現思路。
本案例中站點地圖的實現,并沒有借助MvcSiteMapProvider第三方框架來實現。其實現原理首先獲得用戶的路由請求,然后根據用戶請求根據站點地圖的配置獲得對應的配置節點,接著根據站點地圖的節點信息生成類似">首頁>"這樣帶標簽的字符串;如果獲得的節點是配置文件中某個父節點的子節點,此時會通過遞歸的方式找到其父節點,然后遞歸地生成對應帶標簽的字符串,從而完成站點地圖的功能。分析完實現思路之后,下面讓我們再對照下具體的實現代碼來加深理解。具體的實現代碼如下所示:
```
public class MvcSiteMap
{
private static readonly MvcSiteMap _instance = new MvcSiteMap();
private static readonly XDocument Doc = XDocument.Load(HttpContext.Current.Server.MapPath(@"~/SiteMap.xml"));
private UrlHelper _url = null;
private string _currentUrl;
public static MvcSiteMap Instance
{
get { return _instance;}
}
private MvcSiteMap()
{
}
public MvcHtmlString Navigator()
{
// 獲得當前請求的路由信息
_url = new UrlHelper(HttpContext.Current.Request.RequestContext);
var routeUrl = _url.RouteUrl(HttpContext.Current.Request.RequestContext.RouteData.Values);
if (routeUrl != null)
_currentUrl = routeUrl.ToLower();
// 從配置的站點Xml文件中找到當前請求的Url相同的節點
var c = FindNode(Doc.Root);
var temp = GetPath(c);
return MvcHtmlString.Create(BuildPathString(temp));
}
// 從SitMap配置文件中找到當前請求匹配的節點
private XElement FindNode(XElement node)
{
// 如果xml節點對應的url是否與當前請求的節點相同,如果相同則直接返回xml對應的節點
// 如果不同開始遞歸子節點
return IsUrlEqual(node) == true ? node : RecursiveNode(node);
}
// 判斷xml節點對應的url是否與當前請求的url一樣
private bool IsUrlEqual(XElement c)
{
var a = GetNodeUrl(c).ToLower();
return a == _currentUrl;
}
// 遞歸Xml節點
private XElement RecursiveNode(XElement node)
{
foreach (var c in node.Elements())
{
if (IsUrlEqual(c) == true)
{
return c;
}
else
{
var x = RecursiveNode(c);
if (x != null)
{
return x;
}
}
}
return null;
}
// 獲得xml節點對應的請求url
private string GetNodeUrl(XElement c)
{
return _url.Action(c.Attribute("action").Value, c.Attribute("controller").Value,
new {area = c.Attribute("area").Value});
}
// 根據對應請求url對應的Xml節點獲得其在Xml中的路徑,即獲得其父節點有什么
// SiteMap.xml 中節點的父子節點一定要配置對
private Stack<XElement> GetPath(XElement c)
{
var temp = new Stack<XElement>();
while (c != null)
{
temp.Push(c);
c = c.Parent;
}
return temp;
}
// 根據節點的路徑來拼接帶標簽的字符串
private string BuildPathString(Stack<XElement> m)
{
var sb = new StringBuilder();
var tc = new TagBuilder("span");
tc.SetInnerText(">");
var sp = tc.ToString();
var count = m.Count;
for (var x = 1; x <= count; x++)
{
var c = m.Pop();
TagBuilder tb;
if (x == count)
{
tb = new TagBuilder("span");
}
else
{
tb = new TagBuilder("a");
tb.MergeAttribute("href", GetNodeUrl(c));
}
tb.SetInnerText(c.Attribute("title").Value);
sb.Append(tb);
sb.Append(sp);
}
return sb.ToString();
}
}
```
對應的站點地圖配置信息如下所示:
```
<?xml version="1.0" encoding="utf-8" ?>
<node title="首頁" area="" action="Index" controller="Home">
<node title="我的" area="UserCenter" action="Manage" controller="Account">
<node title="訂單" area="" action="Orders" controller="Home" />
<node title="賬戶" area="" action="Manage" controller="Account" />
<node title="購物車" area="" action="ShoppingCart" controller="Home" />
</node>
<node title="關于" area="AboutCenter" action="About" controller="Home" >
<node title="Online Store 項目" area="" action="About" controller="Home" />
<node title="聯系方式" area="" action="Contact" controller="Home" />
</node>
</node>
```
實現完成之后,下面讓我們具體看看本案例中站點地圖的實現效果看看,具體運行效果如下圖所示:

## 四、小結
到這里,本專題的內容就結束了。本專題主要借助Unity.Interception框架在網上書店中引入了AOP功能,并且最后簡單介紹了站點地圖的實現。在下一專題將對CQRS模式做一個全面的介紹。
本案例所有源碼:[https://github.com/lizhi5753186/OnlineStore_Second/](https://github.com/lizhi5753186/OnlineStore_Second/)
- 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 領域驅動設計實戰系列總結