# [.NET領域驅動設計實戰系列]專題七:DDD實踐案例:引入事件驅動與中間件機制來實現后臺管理功能
## 一、引言
在當前的電子商務平臺中,用戶下完訂單之后,然后店家會在后臺看到客戶下的訂單,然后店家可以對客戶的訂單進行發貨操作。此時客戶會在自己的訂單狀態看到店家已經發貨。從上面的業務邏輯可以看出,當用戶下完訂單之后,店家或管理員可以對客戶訂單進行跟蹤和操作。上一專題我們已經實現創建訂單的功能,則接下來自然就是后臺管理功能的實現了。所以在這一專題中將詳細介紹如何在網上書店案例中實現后臺管理功能。
## 二、后臺管理中的權限管理的實現
后臺管理中,首先需要實現的自然就是權限管理了,因為要進行商品管理等操作的話,則必須對不同的用戶指定的不同角色,然后為不同角色指定不同的權限。這樣才能確保普通用戶不能進行一些后臺操作。
然而角色和權限的賦予一般都是由系統管理員來操作。所以在最開始創建一個管理員用戶,之后就可以以管理員的賬號進行登錄來進行后臺操作的管理,包括添加角色,為用戶分配角色、添加用戶等操作。
這里就牽涉到一個權限管理的問題了。系統如何針對不同用戶的全新進行管理呢?
其權限管理一個實現思路其實如下:
* 不同角色可以看到不同的鏈接,只有指定權限的用戶才可以看到與其對應權限的操作。如只有管理員才可以添加用戶和為用戶賦予權限,而賣家只能對消費者訂單的處理和對自己商店添加商品等操作。
從上面的描述可以發現,權限管理的實現主要包括兩部分:
1. 為不同用戶指定不同的鏈接顯示。如管理員可以看到后臺管理的所有鏈接:包括角色管理,商品管理,用戶管理、訂單管理,商品分類管理,而賣家只能看到訂單管理,商品管理和商品類別管理等。其實現就是為這些鏈接的生成指定不同的權限,只有達到權限用戶才進行生成該鏈接
2. 既然要為不同用戶指定不同的權限,則首先要獲得用戶的權限,然后根據用戶的權限來動態生成對應的鏈接。
有了上面的思路,下面就讓我們一起為網上書店案例加入權限管理的功能:
首先,我在Layout.cshtml頁面加入指定權限的鏈接,具體的代碼如下所示:
```
<table width="996" border="0" cellspacing="0" cellpadding="0" align="center">
<tr>
<td height="607" valign="top">
<table width="996" border="0" cellspacing="0" cellpadding="0">
<tr>
<td width="300" height="55" class="logo"></td>
<td width="480" class="menu">
<ul class="sf-menu">
<li>@Html.ActionLink("首頁", "Index", "Home")</li>
@if (User.Identity.IsAuthenticated)
{
<li>@Html.ActionLink("我的", "Manage", "Account")
<ul>
<li>@Html.ActionLink("訂單", "Orders", "Home")</li>
<li>@Html.ActionLink("賬戶", "Manage", "Account")</li>
<li>@Html.ActionLink("購物車", "ShoppingCart", "Home")</li>
</ul>
</li>
}
**@if (User.Identity.IsAuthenticated)
{** **<li>@Html.ActionLinkWithPermission("管理", "Administration", "Admin", PermissionKeys.Administrators | PermissionKeys.Buyers | PermissionKeys.SalesReps)
<ul>
<li>@Html.ActionLinkWithPermission("銷售訂單管理", "Orders", "Admin", PermissionKeys.Administrators | PermissionKeys.SalesReps)</li>
<li>@Html.ActionLinkWithPermission("商品分類管理", "Categories", "Admin", PermissionKeys.Administrators | PermissionKeys.Buyers)</li>
<li>@Html.ActionLinkWithPermission("商品信息管理", "Products", "Admin", PermissionKeys.Administrators | PermissionKeys.Buyers)</li>
<li>@Html.ActionLinkWithPermission("用戶賬戶管理", "UserAccounts", "Admin", PermissionKeys.Administrators)</li>
<li>@Html.ActionLinkWithPermission("用戶角色管理", "Roles", "Admin", PermissionKeys.Administrators)</li>
</ul>
</li>
}** <li>@Html.ActionLink("關于", "About", "Home")
<ul>
<li>@Html.ActionLink("Online Store 項目", "About", "Home")</li>
<li>@Html.ActionLink("聯系方式", "Contact", "Home")</li>
</ul>
</li>
</ul>
</td>
<td width="216" class="menu">
@{Html.RenderAction("_LoginPartial", "Layout");}
</td>
</tr>
</table>
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td width="100%" height="10px" />
</tr>
</table>
<table width="996" border="0" cellspacing="0" cellpadding="0">
<tr>
<td>
<img src="/images/header.jpg" alt="" width="996" height="400" border="0"></td>
</tr>
</table>
<table width="996" border="0" cellspacing="0" cellpadding="0">
<tr align="left" valign="top">
<td width="202" height="334">
@{Html.RenderAction("CategoriesPartial", "Layout");}
</td>
<td width="20"> </td>
<td width="774">
<table width="774" border="0" cellspacing="0" cellpadding="0">
<tr>
@(MvcSiteMap.Instance.Navigator())
</tr>
<tr>
<td>
@RenderBody()
</td>
</tr>
</table>
</td>
</tr>
</table>
<table width="996" border="0" cellspacing="0" cellpadding="0">
<tr>
<td>
<img src="/images/footer.jpg" alt="" width="996" height="5"></td>
</tr>
<tr>
<td height="76">
<table width="996" border="0" cellspacing="0" cellpadding="0" align="center">
<tr>
<td width="329" height="78" align="right"></td>
<td width="14"> </td>
<td width="653"><span class="style7">@Html.ActionLink("主頁", "Index", "Home") | @Html.ActionLink("所有分類", "Category", "Home", null, null) | @Html.ActionLink("我的賬戶", "Account", "Account") | @Html.ActionLink("聯系我們", "Contact", "Home") | @Html.ActionLink("關于本站", "About", "Home")</span><br>
版權所有 © 2014-2015, Online Store, 保留所有權利。 </td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
```
上面紅色加粗部分就是設置不同角色的不同權限。其中ActionLinkWithPermission是一個擴展方法,其具體實現就是獲得登陸用戶的角色,然后把用戶的角色與當前的需要權限進行比較,如果相同,則通過HtmlHelper.GenerateLink方法來生成對應的鏈接。該方法的實現代碼如下所示:
```
public static MvcHtmlString ActionLinkWithPermission(this HtmlHelper helper, string linkText, string action, string controller, PermissionKeys required)
{
if (helper == null ||
helper.ViewContext == null ||
helper.ViewContext.RequestContext == null ||
helper.ViewContext.RequestContext.HttpContext == null ||
helper.ViewContext.RequestContext.HttpContext.User == null ||
helper.ViewContext.RequestContext.HttpContext.User.Identity == null)
return MvcHtmlString.Empty;
using (var proxy = new UserServiceClient())
{
var role = proxy.GetRoleByUserName(helper.ViewContext.RequestContext.HttpContext.User.Identity.Name);
if (role == null)
return MvcHtmlString.Empty;
var keyName = role.Name;
var permissionKey = (PermissionKeys)Enum.Parse(typeof(PermissionKeys), keyName);
// 通過用戶的角色和對應對應的權限進行與操作
// 與結果等于用戶角色時,表示用戶角色與所需要的權限一樣,則創建對應權限的鏈接
return (permissionKey & required) == permissionKey ?
MvcHtmlString.Create(HtmlHelper.GenerateLink(helper.ViewContext.RequestContext, helper.RouteCollection, linkText, null, action, controller, null, null))
: MvcHtmlString.Empty;
}
}
```
通過上面的代碼,我們就已經完成了權限管理的實現了。
## 三、后臺管理中商品管理的實現
如果你是管理員的話,這樣你就可以進入后臺頁面對商品、用戶、訂單等進行管理了。在上面我們已經完成了權限管理的實現。接下來,我們可以用一個管理員賬號登陸之后,你可以看到管理員對應的權限。這里我直接在數據庫中添加了一條管理員賬號,其賬號信息是admin,密碼也是admin。下面我就這個賬號后看到的界面如下圖所示:

從上圖可以看出,后臺管理包括銷售訂單管理、商品類別管理、商品信息管理等。這些都是一些類似的實現,都是一些增、刪、改功能的實現。這里就是商品信息管理為例來介紹下。點擊商品信息管理后,將可以看到所有商品列表,在該頁面可以進商品進行添加、修改和刪除等操作。其實現主要是通過應用服務來調用倉儲來實現商品的信息的持久化罷了,下面就具體介紹下商品添加功能的實現。因為商品的添加需要首先把上傳的圖片先添加到服務器上的Images文件夾下,然后通過控制器來調用ProductService的CreateProducts方法來把商品保存到數據庫中。
首先是圖片上傳功能的實現,其實現代碼如下所示:
```
[HandleError]
public class AdminController : ControllerBase
{
#region Common Utility Actions
// 保存圖片到服務器指定目錄下
[NonAction]
private void SaveFile(HttpPostedFileBase postedFile, string filePath, string saveName)
{
string phyPath = Request.MapPath("~" + filePath);
if (!Directory.Exists(phyPath))
{
Directory.CreateDirectory(phyPath);
}
try
{
postedFile.SaveAs(phyPath + saveName);
}
catch (Exception e)
{
throw new ApplicationException(e.Message);
}
}
// 圖片上傳功能的實現
[HttpPost]
public ActionResult Upload(HttpPostedFileBase fileData, string folder)
{
var result = string.Empty;
if (fileData != null)
{
string ext = Path.GetExtension(fileData.FileName);
result = Guid.NewGuid()+ ext;
SaveFile(fileData, Url.Content("~/Images/Products/"), result);
}
return Content(result);
}
}
```
圖片上傳成功之后,接下來點擊保存按鈕則把商品進行持久化到數據庫中。其實現邏輯主要是調用商品倉儲的實現類來完成商品的添加。主要的實現代碼如下所示:
```
[HandleError]
public class AdminController : ControllerBase
{
[HttpPost]
[Authorize]
public ActionResult AddProduct(ProductDto product)
{
using (var proxy = new ProductServiceClient())
{
if (string.IsNullOrEmpty(product.ImageUrl))
{
var fileName = Guid.NewGuid() + ".png";
System.IO.File.Copy(Server.MapPath("~/Images/Products/ProductImage.png"), Server.MapPath(string.Format("~/Images/Products/{0}", fileName)));
product.ImageUrl = fileName;
}
var addedProducts = proxy.CreateProducts(new List<ProductDto> { product }.ToArray());
if (product.Category != null &&
product.Category.Id != Guid.Empty.ToString())
proxy.CategorizeProduct(new Guid(addedProducts[0].Id), new Guid(product.Category.Id));
return RedirectToSuccess("添加商品信息成功!", "Products", "Admin");
}
}
}
// 商品服務的實現
public class ProductServiceImp : ApplicationService, IProductService
{
public List<ProductDto> CreateProducts(List<ProductDto> productsDtos)
{
return PerformCreateObjects<List<ProductDto>, ProductDto, Product>(productsDtos, _productRepository);
}
}
```
到此,我們已經完成了商品添加功能的實現,下面讓我們看看商品添加的具體效果如何。添加商品頁面:

點擊保存更改按鈕后,則進行商品的添加,添加成功后界面效果:

## 四、后臺管理中發貨操作和確認收貨的實現
當消費者創建訂單之后,然后賣家或管理員可以通過訂單管理頁面來對訂單進行發貨處理操作。以通知購買者該商品已發貨了。在當前的電子商務網站中,除了更新訂單的狀態外,還會發郵件或短信通知購買者。為了保證這兩個操作同時完成,此時需要將這兩個放在同一個事務中進行提交。
這里為了使系統有更好地可擴展性,采用了基于消息隊列和事件驅動的方式來完成發貨操作。在看具體實現代碼之前,我們先來分析下實現思路:
* 賣家或管理員在訂單管理頁面,點擊發貨按鈕后,此時相當于訂單的狀態進行了更新,從已付款狀態到已發貨狀態。這里當然你可以采用傳統的方式來實現,即調用訂單倉儲來更新對應訂單的狀態。但是這樣的實現方式,郵件發送操作可能會嵌套在應用服務層了。這樣的設計顯然不適合擴展。所以這里采用基于事件驅動和消息隊列方式來改進這種方式。
1. 首先,當商家點擊發貨操作,此時會產生一個發貨事件;
2. 接著由注冊的領域事件處理程序進行對該領域事件處理,處理邏輯主要是更新訂單的狀態和更新時間;
3. 然后再將該事件發布到EventBus,EventBus中保存了一個隊列來存放事件,發布操作的實現就是往該隊列插入一個待處理的事件;
4. 最后在EventBus中的Commit方法中對隊列中的事件進行出隊列操作,通過事件聚合類來獲得對應事件處理器來對出隊列的事件進行處理。
* 事件聚合器通過Unity注入(應用)事件的處理器。在EventAggregator類中定義_eventHandlers來保存所有(應用)事件的處理器,在EventAggregator的構造函數中通過調用其Register方法把對應的事件處理器添加到_eventHandlers字典中。然后在EventBus中的Commit方法中通過找到EventAggregator中的Handle方法來觸發事件處理器來處理對應事件,即發出郵件通知。這里事件聚合器起到映射的功能,映射應用事件到對應的事件處理器來處理。
通過上面的分析可以發現,發貨操作和收貨操作都涉及2類事件,一類是領域事件,另一類處于應用事件,領域事件的處理由領域事件處理器來處理,而應用事件的處理不能定義在領域層,所以我們這里新建了一個應用事件處理層,叫OnlineStore.Events.Handlers,已經新建了一個對EventBus支持的層,叫OnlineStore.Events。經過上面的分析,實現發貨操作和收貨操作是不是有點清晰了呢?如果不是的話也沒關系,我們可以結合下面具體的實現代碼再來理解下上面分析的思路。因為收貨操作和發貨操作的實現非常類似,這里只貼出發貨操作實現的主要代碼進行演示。
首先是AdminController中DispatchOrder操作的實現:
```
public ActionResult DispatchOrder(string id)
{
using (var proxy = new OrderServiceClient())
{
proxy.Dispatch(new Guid(id));
return RedirectToSuccess(string.Format("訂單 {0} 已成功發貨!", id.ToUpper()), "Orders", "Admin");
}
}
```
接下來便是OrderService中Dispatch方法的實現:
```
public void Dispatch(Guid orderId)
{
using (var transactionScope = new TransactionScope())
{
var order = _orderRepository.GetByKey(orderId);
order.Dispatch();
_orderRepository.Update(order);
RepositorytContext.Commit();
_eventBus.Commit();
transactionScope.Complete();
}
}
```
下面是Order實體類中Dispatch方法的實現:
```
/// <summary>
/// 處理發貨。
/// </summary>
public void Dispatch()
{
// 處理領域事件
DomainEvent.Handle<OrderDispatchedEvent>(new OrderDispatchedEvent(this) { DispatchedDate = DateTime.Now, OrderId = this.Id, UserEmailAddress = this.User.Email });
}
```
接下來便是領域事件中Handle方法的實現了,其實現邏輯就是獲得所有已注冊的領域事件處理器,然后分別事件處理器進行調用。具體的實現代碼如下所示:
```
public static void Handle<TDomainEvent>(TDomainEvent domainEvent)
where TDomainEvent : class, IDomainEvent
{
// 找到對應的事件處理器來對事件進行處理
var handlers = ServiceLocator.Instance.ResolveAll<IDomainEventHandler<TDomainEvent>>();
foreach (var handler in handlers)
{
if (handler.GetType().IsDefined(typeof(HandlesAsynchronouslyAttribute), false))
Task.Factory.StartNew(() => handler.Handle(domainEvent));
else
handler.Handle(domainEvent);
}
}
```
對應OrderDispatchedEventHandler類中Handle方法的實現如下所示:
```
// 發貨事件處理器
public class OrderDispatchedEventHandler : IDomainEventHandler<OrderDispatchedEvent>
{
private readonly IEventBus _bus;
public OrderDispatchedEventHandler(IEventBus bus)
{
_bus = bus;
}
public void Handle(OrderDispatchedEvent @event)
{
// 獲得事件源對象
var order = @event.Source as Order;
// 更新事件源對象的屬性
if (order == null) return;
order.DispatchedDate = @event.DispatchedDate;
order.Status = OrderStatus.Dispatched;
// 這里把領域事件認為是一種消息,推送到EventBus中進行進一步處理。
_bus.Publish<OrderDispatchedEvent>(@event);
}
}
```
從上面代碼中可以發現,領域事件處理器中只是簡單更新訂單狀態的狀態為Dispatched和更新訂單發貨時間,之后就把該事件繼續發布到EventBus中進一步進行處理。EventBus類的具體實現代碼如下所示:
```
// 領域事件處理器只是對事件對象的狀態進行更新
// 后續的事件處理操作交給EventBus進行處理
// 本案例中EventBus主要處理的任務就是發送郵件通知,
// 在EventBus一般處理應用事件,而領域事件處理器一般處理領域事件
public class EventBus : DisposableObject, IEventBus
{
public EventBus(IEventAggregator aggregator)
{
this._aggregator = aggregator;
// 獲得EventAggregator中的Handle方法
_handleMethod = (from m in aggregator.GetType().GetMethods()
let parameters = m.GetParameters()
let methodName = m.Name
where methodName == "Handle" &&
parameters != null &&
parameters.Length == 1
select m).First();
}
public void Publish<TMessage>(TMessage message)
where TMessage : class, IEvent
{
_messageQueue.Value.Enqueue(message);
_committed.Value = false;
}
// 觸發應用事件處理器對事件進行處理
public void Commit()
{
while (_messageQueue.Value.Count > 0)
{
var evnt = _messageQueue.Value.Dequeue();
var evntType = evnt.GetType();
var method = _handleMethod.MakeGenericMethod(evntType);
// 調用應用事件處理器來對應用事件進行處理
method.Invoke(_aggregator, new object[] { evnt });
}
_committed.Value = true;
}
}
```
其EventAggregator類的實現如下所示:
```
public class EventAggregator : IEventAggregator
{
private readonly object _sync = new object();
private readonly Dictionary<Type, List<object>> _eventHandlers = new Dictionary<Type, List<object>>();
private readonly MethodInfo _registerEventHandlerMethod;
public EventAggregator()
{
// 通過反射獲得EventAggregator的Register方法
_registerEventHandlerMethod = (from p in this.GetType().GetMethods()
let methodName = p.Name
let parameters = p.GetParameters()
where methodName == "Register" &&
parameters != null &&
parameters.Length == 1 &&
parameters[0].ParameterType.GetGenericTypeDefinition() == typeof(IEventHandler<>)
select p).First();
}
public EventAggregator(object[] handlers)
: this()
{
// 遍歷注冊的EventHandler來把配置文件中具體的EventHanler通過Register添加進_eventHandlers字典中
foreach (var obj in handlers)
{
var type = obj.GetType();
var implementedInterfaces = type.GetInterfaces();
foreach (var implementedInterface in implementedInterfaces)
{
if (implementedInterface.IsGenericType &&
implementedInterface.GetGenericTypeDefinition() == typeof(IEventHandler<>))
{
var eventType = implementedInterface.GetGenericArguments().First();
var method = _registerEventHandlerMethod.MakeGenericMethod(eventType);
// 調用Register方法將EventHandler添加進_eventHandlers字典中
method.Invoke(this, new object[] { obj });
}
}
}
}
public void Register<TEvent>(IEventHandler<TEvent> eventHandler)
where TEvent : class, IEvent
{
lock (_sync)
{
var eventType = typeof(TEvent);
if (_eventHandlers.ContainsKey(eventType))
{
var handlers = _eventHandlers[eventType];
if (handlers != null)
{
handlers.Add(eventHandler);
}
else
{
handlers = new List<object> {eventHandler};
}
}
else
_eventHandlers.Add(eventType, new List<object> { eventHandler });
}
}
public void Register<TEvent>(IEnumerable<IEventHandler<TEvent>> eventHandlers)
where TEvent : class, IEvent
{
foreach (var eventHandler in eventHandlers)
Register<TEvent>(eventHandler);
}
// 調用具體的EventHanler的Handle方法來對事件進行處理
public void Handle<TEvent>(TEvent evnt)
where TEvent : class, IEvent
{
if (evnt == null)
throw new ArgumentNullException("evnt");
var eventType = evnt.GetType();
if (_eventHandlers.ContainsKey(eventType) &&
_eventHandlers[eventType] != null &&
_eventHandlers[eventType].Count > 0)
{
var handlers = _eventHandlers[eventType];
foreach (var handler in handlers)
{
var eventHandler = handler as IEventHandler<TEvent>;
if(eventHandler == null)
continue;
// 異步處理
if (eventHandler.GetType().IsDefined(typeof(HandlesAsynchronouslyAttribute), false))
{
Task.Factory.StartNew((o) => eventHandler.Handle((TEvent)o), evnt);
}
else
{
eventHandler.Handle(evnt);
}
}
}
}
```
至于確認收貨操作的實現也是類似,大家可以自行參考Github源碼進行實現。到此,我們商品發貨和確認收貨的功能就實現完成了。此時,我們解決方案已經調整為:

經過本專題后,我們網上書店案例的業務功能都完成的差不多了,后面添加的一些功能都是附加功能,例如分布式緩存的支持、分布式消息隊列的支持以及面向切面編程的支持等功能。既然業務功能都完成的差不多了,下面讓我們具體看看發貨操作的實現效果吧。
首先是銷售訂單管理首頁,在這里可以看到所有用戶的訂單狀態。具體效果如下圖示所示:

點擊上圖的發貨按鈕后便可以完成商品發貨操作,此時創建該訂單的用戶郵箱中會收到一份發貨郵件通知,具體實現效果截圖如下所示:

其確認收貨操作實現的效果與發貨操作的效果差不多,這里就不一一截圖了,大家可以自行到github上下載源碼進行運行查看。
## 五、總結
到這里,該專題的介紹的內容就結束。本專題主要介紹后臺管理中權限管理的實現、商品管理、類別管理、角色管理、用戶角色管理和訂單管理等功能。正如上面所說的,到此,本網上書店的DDD案例一些業務功能都實現的差不多了,接下來需要完善的功能主要是一些附加功能,這些功能主要是為了提高網站的可擴展性和可伸縮性。這些主要包括緩存的支持、分布式消息隊列的支持以及AOP的支持。在下一個專題將介紹分布式緩存和分布式消息隊列的支持,請大家繼續關注。
本專題的所有源碼下載:[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 領域驅動設計實戰系列總結