# [.NET領域驅動設計實戰系列]專題二:結合領域驅動設計的面向服務架構來搭建網上書店
## 一、前言
在前面專題一中,我已經介紹了我寫這系列文章的初衷了。由于dax.net中的DDD框架和Byteart Retail案例并沒有對其形成過程做一步步分析,而是把整個DDD的實現案例展現給我們,這對于一些剛剛接觸領域驅動設計的朋友可能會非常迷茫,從而覺得領域驅動設計很難,很復雜,因為學習中要消化一個整個案例的知識,這樣未免很多人消化不了就打退堂鼓,就不繼續研究下去了,所以這樣也不利于DDD的推廣。然而本系列可以說是剛接觸領域驅動設計朋友的福音,本系列將結合領域驅動設計的思想來一步步構建一個網上書店,從而讓大家學習DDD不再枯燥和可以看到一個DDD案例的形成歷程。最后,再DDD案例完成之后,將從中抽取一個領域驅動的框架,從而大家也可以看到一個DDD框架的形成歷程,這樣就不至于一下子消化一整個框架和案例的知識,而是一步步消化。接下來,該專題將介紹的是:結合領域驅動設計的SOA架構來構建網上書店,本專題中并沒有完成網上書店的所有頁面和覆蓋DDD中的所有內容,而只是一部分,后面的專題將會在本專題的網上書店進行一步步完善,通過一步步引入DDD的內容和重構來完成整個項目。
## 二、DDD分層架構
從概念上說,領域驅動設計架構主要分為四層,分別為:基礎設施層、領域層、應用層和表現層。
* 基礎結構層:該層專為其他各層提供各項通用技術框架支持。像一些配置文件處理、緩存處理,事務處理等都可以放在這里。
* 領域層:簡單地說就是業務所涉及的領域對象(包括實體、值對象)、領域服務等。該層就是所謂的領域模型了,領域驅動設計提倡是富領域模型,富領域模型指的是:盡量將業務邏輯放在歸屬于它的領域對象中。而之前的三層架構中的領域模型都是貧血領域模型,因為在三層中的領域模型只包含業務屬性,而不包含任何業務邏輯。本專題的網上書店領域模型目前還沒有包含任何業務邏輯,在后期將會完善。
**實體可以認為對應于數據庫的表,而值對象一般定義在實體類中。**
* 應用層:該層不包含任何領域邏輯,它主要用來對任務進行協調,它構建了表現層和領域層的橋梁。SOA架構就是在該層進行實現的。
* 表現層:指的是用戶界面,例如Asp.net mvc網站,WPF、Winform和控制臺等。它主要用來想用戶展現內容。
下面用一個圖來形象展示DDD的分層架構:

本系列介紹的領域驅動設計實戰,則自然少了領域驅動設計分層架構的實現了,上面簡單介紹了領域驅動的分層架構,接下來將詳細介紹在網上書店中各層是如何去實現的。
## 三、網上書店領域模型層的實現
在應用領域驅動設計的思想來構建一個項目,則第一步就是了解需求,明白項目的業務邏輯,了解清楚業務邏輯后,則把業務邏輯抽象成領域對象,領域對象所放在的位置也就是領域模型層了。該專題介紹的網上書店主要完成了商品所涉及的頁面,包括商品首頁,單個商品的詳細信息等。所以這里涉及的領域實體包括2個,一個是商品類,另外一個就是類別類,因為在商品首頁中,需要顯示所有商品的類別。在給出領域對象的實現之前,這里需要介紹領域層中所涉及的幾個概念。
* 聚合根:聚合根也是實體,但與實體不同的是,聚合根是由實體和值對象組成的系統邊界對象。舉個例子來說,例如訂單和訂單項,根據業務邏輯,我們需要跟蹤訂單和訂單項的狀態,所以設計它們都為實體,但只有訂單才是聚合根對象,而訂單項不是,因為訂單項只有在訂單中才有意義,意思就是說:用戶不能直接看到訂單項,而是先查詢到訂單,然后再看到該訂單下的訂單項。所以聚合根可以理解為用戶直接操作的對象。在這里商品類和類別類都是一個聚合根。
根據面向接口編程原則,我們在領域模型中應該定義一個實體接口和聚合根接口,而因為聚合根也是屬于實體,所以聚合根接口繼承于實體接口,而商品類和類別類都是聚合根,所以它們都實現聚合根接口。如果像訂單項只是實體不是聚合根的類則實現實體接口。有了上面的分析,則領域模型層的實現也就自然出來了,下面是領域對象的具體實現:
```
// 商品類
public class Product : AggregateRoot
{
public string Name { get; set; }
public string Description { get; set; }
public decimal UnitPrice { get; set; }
public string ImageUrl { get; set; }
public bool IsNew{ get; set; }
public override string ToString()
{
return Name;
}
}
```
```
// 類別類
public class Category : AggregateRoot
{
public string Name { get; set; }
public string Description { get; set; }
public override string ToString()
{
return this.Name;
}
}
```
另外,領域層除了實現領域對象外,還需要定義倉儲接口,而倉儲層則是對倉儲接口的實現。倉儲可以理解為在內存中維護一系列聚合根的集合,而聚合根不可能一直存在于內存中,當它不活動時會被持久化到數據中。而倉儲層完成的任務是持久化聚合根對象到數據或從數據庫中查詢存儲的對象來重新創建領域對象。
倉儲層有幾個需要明確的概念:
1. 倉儲里面存放的對象一定是聚合根,因為領域模型是以聚合根的概念去劃分的,聚合根就是我們操作對象的一個邊界。所以我們都是對某個聚合根進行操作的,而不存在對聚合內的值對象進行操作。因此,倉儲只針對聚合根設計。
2. 因為倉儲只針對聚合根設計,所以一個聚合根需要實現一個倉儲。
3. 不要把倉儲簡單理解為DAO,倉儲屬于領域模型的一部分,代表了領域模型向外界提供接口的一部分,而DAO是表示數據庫向上層提供的接口表示。一個是針對領域模型而言,而另一個針對數據庫而言。兩者側重點不一樣。
4. 倉儲分為定義部分和實現部分,在領域模型中定義倉儲的接口,**而在基礎設施層實現具體的倉儲**。這樣做的原因是:由于倉儲背后的實現都是在和數據庫打交道,但是我們又不希望客戶(如應用層)把重點放在如何從數據庫獲取數據的問題上,因為這樣做會導致客戶(應用層)代碼很混亂,很可能會因此而忽略了領域模型的存在。所以我們需要提供一個簡單明了的接口,供客戶使用,確保客戶能以最簡單的方式獲取領域對象,從而可以讓它專心的不會被什么數據訪問代碼打擾的情況下協調領域對象完成業務邏輯。這種通過接口來隔離封裝變化的做法其實很常見。由于客戶面對的是抽象的接口并不是具體的實現,所以我們可以隨時替換倉儲的真實實現,這很有助于我們做單元測試。在本專題的案例中,我們把倉儲層的實現單獨從基礎設施層拎出來了,作為一個獨立的層存在。這也就是為什么DDD分層中沒有定義倉儲層啊,而本專題的案例中多了一個倉儲層的實現。
5. 倉儲在設計查詢接口時,會經常用到規約模式(Specification Pattern)。本專題的案例中沒有給出,這點將會在后面專題添加上去。
6. 倉儲一般不負責事務處理,一般事務處理會交給“工作單元(Unit Of Work)”去處理,同樣本專題也沒有涉及工作單元的實現,這點同樣會在后面專題繼續完善。這里列出來讓大家對后面的專題可以有個清晰的概念,而不至于是空穴來風的。
介紹完倉儲之后,接下來就在領域層中定義倉儲接口,因為本專題中涉及到2個聚合根,則自然需要實現2個倉儲接口。根據面向接口編程原則,我們讓這2個倉儲接口都實現與一個公共的接口:IRepository接口。另外倉儲接口還需要定義一個倉儲上下接口,因為在Entity Framework中有一個[DbContex](https://msdn.microsoft.com/zh-cn/library/system.data.entity.dbcontext%28v=vs.113%29.aspx?f=255&MSPPError=-2147217396)類,所以我們需要定義一個EntityFramework上下文對象來對DbContex進行包裝。也就自然有了倉儲上下文接口了。經過上面的分析,倉儲接口的實現也就一目了然了。
```
// 倉儲接口
public interface IRepository<TAggregateRoot>
where TAggregateRoot :class, IAggregateRoot
{
void Add(TAggregateRoot aggregateRoot);
IEnumerable<TAggregateRoot> GetAll();
// 根據聚合根的ID值,從倉儲中讀取聚合根
TAggregateRoot GetByKey(Guid key);
}
```
這樣我們就完成了領域層的搭建了,接下面,我們就需要對領域層中定義的倉儲接口進行實現了。我這里將倉儲接口的實現單獨弄出了一個層,當然你也可以放在基礎設施層中的Repositories文件夾中。不過我看很多人都直接拎出來的。我這里也是直接作為一個層。
## 四、網上書店Repository(倉儲)層的實現
定義完倉儲接口之后,接下來就是在倉儲層實現這些接口,完成領域對象的序列化。首先是產品倉儲的實現:
```
// 商品倉儲的實現
public class ProductRepository : IProductRepository
{
#region Private Fields
private readonly IEntityFrameworkRepositoryContext _efContext;
#endregion
#region Public Properties
public IEntityFrameworkRepositoryContext EfContext
{
get { return this._efContext; }
}
#endregion
#region Ctor
public ProductRepository(IRepositoryContext context)
{
var efContext = context as IEntityFrameworkRepositoryContext;
if (efContext != null)
this._efContext = efContext;
}
#endregion
public IEnumerable<Product> GetNewProducts(int count = 0)
{
var ctx = this.EfContext.DbContex as OnlineStoreDbContext;
if (ctx == null)
return null;
var query = from p in ctx.Products
where p.IsNew == true
select p;
if (count == 0)
return query.ToList();
else
return query.Take(count).ToList();
}
public void Add(Product aggregateRoot)
{
throw new NotImplementedException();
}
public IEnumerable<Product> GetAll()
{
var ctx = this.EfContext.DbContex as OnlineStoreDbContext;
if (ctx == null)
return null;
var query = from p in ctx.Products
select p;
return query.ToList();
}
public Product GetByKey(Guid key)
{
return EfContext.DbContex.Products.First(p => p.Id == key);
}
}
```
接下來是類別倉儲的實現:
```
// 類別倉儲的實現
public class CategoryRepository :ICategoryRepository
{
#region Private Fields
private readonly IEntityFrameworkRepositoryContext _efContext;
public CategoryRepository(IRepositoryContext context)
{
var efContext = context as IEntityFrameworkRepositoryContext;
if (efContext != null)
this._efContext = efContext;
}
#endregion
#region Public Properties
public IEntityFrameworkRepositoryContext EfContext
{
get { return this._efContext; }
}
#endregion
public void Add(Category aggregateRoot)
{
throw new System.NotImplementedException();
}
public IEnumerable<Category> GetAll()
{
var ctx = this.EfContext.DbContex as OnlineStoreDbContext;
if (ctx == null)
return null;
var query = from c in ctx.Categories
select c;
return query.ToList();
}
public Category GetByKey(Guid key)
{
return this.EfContext.DbContex.Categories.First(c => c.Id == key);
}
}
```
由于后期除了實現基于EF倉儲的實現外,還想實現基于MongoDb倉儲的實現,所以在倉儲層中創建了一個EntityFramework的文件夾,并定義了一個IEntityFrameworkRepositoryContext接口來繼承于IRepositoryContext接口,由于EF中持久化數據主要是由DbContext對象來完成了,為了有自己框架模型,我在這里定義了OnlineStoreDbContext來繼承DbContext,從而用OnlineStoreDbContext來對DbContext進行了一次包裝。經過上面的分析之后,接下來對于實現也就一目了然了。首先是OnlineStoreDbContext類的實現:
```
public sealed class OnlineStoreDbContext : DbContext
{
#region Ctor
public OnlineStoreDbContext()
: base("OnlineStore")
{
this.Configuration.AutoDetectChangesEnabled = true;
this.Configuration.LazyLoadingEnabled = true;
}
#endregion
#region Public Properties
public DbSet<Product> Products
{
get { return this.Set<Product>(); }
}
public DbSet<Category> Categories
{
get { return this.Set<Category>(); }
}
// 后面會繼續添加屬性,針對每個聚合根都會定義一個DbSet的屬性
// ...
#endregion
}
```
接下來就是IEntityFrameworkRepositoryContext接口的定義以及它的實現了。具體代碼如下所示:
```
public class EntityFrameworkRepositoryContext : IEntityFrameworkRepositoryContext
{
// 引用我們定義的OnlineStoreDbContext類對象
public OnlineStoreDbContext DbContex
{
get { return new OnlineStoreDbContext(); }
}
}
```
這樣,我們的倉儲層也就完成了。接下來就是應用層的實現。
## 五、網上書店應用層的實現
應用層應用了面向服務結構進行實現,采用了微軟面向服務的實現WCF來完成的。網上書店的整個架構完全遵循著領域驅動設計的分層架構,用戶通過UI層(這里實現的是Web頁面)來進行操作,然后UI層調用應用層來把服務進行分發,通過調用基礎設施層中倉儲實現來對領域對象進行持久化和重建。這里應用層主要采用WCF來實現的,其中引用了倉儲接口。針對服務而言,首先就需要定義服務契約了,這里我把服務契約的定義單獨放在了一個服務契約層,當然你也可以在應用層中創建一個服務契約文件夾。首先就去看看服務契約的定義:
```
// 商品服務契約的定義
[ServiceContract(Namespace="")]
public interface IProductService
{
#region Methods
// 獲得所有商品的契約方法
[OperationContract]
IEnumerable<Product> GetProducts();
// 獲得新上市的商品的契約方法
[OperationContract]
IEnumerable<Product> GetNewProducts(int count);
// 獲得所有類別的契約方法
[OperationContract]
IEnumerable<Category> GetCategories();
// 根據商品Id來獲得商品的契約方法
[OperationContract]
Product GetProductById(Guid id);
#endregion
}
```
接下來就是服務契約的實現,服務契約的實現我放在應用層中,具體的實現代碼如下所示:
```
// 商品服務的實現
public class ProductServiceImp : IProductService
{
#region Private Fields
private readonly IProductRepository _productRepository;
private readonly ICategoryRepository _categoryRepository;
#endregion
#region Ctor
public ProductServiceImp(IProductRepository productRepository, ICategoryRepository categoryRepository)
{
_categoryRepository = categoryRepository;
_productRepository = productRepository;
}
#endregion
#region IProductService Members
public IEnumerable<Product> GetProducts()
{
return _productRepository.GetAll();
}
public IEnumerable<Product> GetNewProducts(int count)
{
return _productRepository.GetNewProducts(count);
}
public IEnumerable<Category> GetCategories()
{
return _categoryRepository.GetAll();
}
public Product GetProductById(Guid id)
{
var product = _productRepository.GetByKey(id);
return product;
}
#endregion
}
```
最后就是創建WCF服務來調用服務契約實現了。創建一個后綴為.svc的WCF服務文件,WCF服務的具體實現如下所示:
```
// 商品WCF服務
public class ProductService : IProductService
{
// 引用商品服務接口
private readonly IProductService _productService;
public ProductService()
{
_productService = ServiceLocator.Instance.GetService<IProductService>();
}
public IEnumerable<Product> GetProducts()
{
return _productService.GetProducts();
}
public IEnumerable<Product> GetNewProducts(int count)
{
return _productService.GetNewProducts(count);
}
public IEnumerable<Category> GetCategories()
{
return _productService.GetCategories();
}
public Product GetProductById(Guid id)
{
return _productService.GetProductById(id);
}
}
```
到這里我們就完成了應用層面向服務架構的實現了。從商品的WCF服務實現可以看到,我們有一個ServiceLocator的類。這個類的實現采用服務定位器模式,關于該模式的介紹可以參考d[ax.net的服務定位器模式](http://www.cnblogs.com/daxnet/archive/2013/01/05/2846055.html)的介紹。該類的作用就是調用方具體的實例,簡單地說就是通過服務接口定義具體服務接口的實現,將該實現返回給調用者的。這個類我這里放在了基礎設施層來實現。目前基礎設施層只有這一個類的實現,后期會繼續添加其他功能,例如緩存功能的支持。
另外,在這里使用了Unity依賴注入容器來對接口進行注入。主要的配置文件如下所示:
```
<configuration>
<configSections>
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework"/>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
</configSections>
<!-- Entity Framework 配置信息-->
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework">
<parameters>
<parameter value="Data Source=(LocalDb)\v11.0; Initial Catalog=OnlineStore; Integrated Security=True; Connect Timeout=120; MultipleActiveResultSets=True; AttachDBFilename=|DataDirectory|\OnlineStore.mdf"/>
</parameters>
</defaultConnectionFactory>
</entityFramework>
<!--Unity的配置信息-->
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<container>
<!--倉儲接口的注冊-->
<register type="OnlineStore.Domain.Repositories.IRepositoryContext, OnlineStore.Domain" mapTo="OnlineStore.Repositories.EntityFramework.EntityFrameworkRepositoryContext, OnlineStore.Repositories"/>
<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.ServiceContracts.IProductService, OnlineStore.ServiceContracts" mapTo="OnlineStore.Application.ServiceImplementations.ProductServiceImp, OnlineStore.Application"/>
</container>
</unity>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5"/>
<httpRuntime targetFramework="4.5.1"/>
</system.web>
<!--WCF 服務的配置信息-->
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="OnlineStore.Application.ServiceImplementations.ProductServiceImp" behaviorConfiguration="">
<endpoint address="" binding="wsHttpBinding" contract="OnlineStore.ServiceContracts.IProductService"/>
<!--<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />-->
</service>
</services>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
```
## 六、基礎設施層的實現
基礎設施層在本專題中只包含了服務定位器的實現,后期會繼續添加對其他功能的支持,ServiceLocator類的具體實現如下所示:
```
// 服務定位器的實現
public class ServiceLocator : IServiceProvider
{
private readonly IUnityContainer _container;
private static ServiceLocator _instance = new ServiceLocator();
private ServiceLocator()
{
_container = new UnityContainer();
_container.LoadConfiguration();
}
public static ServiceLocator Instance
{
get { return _instance; }
}
#region Public Methods
public T GetService<T>()
{
return _container.Resolve<T>();
}
public IEnumerable<T> ResolveAll<T>()
{
return _container.ResolveAll<T>();
}
public T GetService<T>(object overridedArguments)
{
var overrides = GetParameterOverrides(overridedArguments);
return _container.Resolve<T>(overrides.ToArray());
}
public object GetService(Type serviceType, object overridedArguments)
{
var overrides = GetParameterOverrides(overridedArguments);
return _container.Resolve(serviceType, overrides.ToArray());
}
#endregion
#region Private Methods
private IEnumerable<ParameterOverride> GetParameterOverrides(object overridedArguments)
{
var overrides = new List<ParameterOverride>();
var argumentsType = overridedArguments.GetType();
argumentsType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.ToList()
.ForEach(property =>
{
var propertyValue = property.GetValue(overridedArguments, null);
var propertyName = property.Name;
overrides.Add(new ParameterOverride(propertyName, propertyValue));
});
return overrides;
}
#endregion
#region IServiceProvider Members
public object GetService(Type serviceType)
{
return _container.Resolve(serviceType);
}
#endregion
}
```
## 七、UI層的實現
根據領域驅動的分層架構,接下來自然就是UI層的實現了,這里UI層的實現采用Asp.net MVC 技術來實現的。UI層主要包括商品首頁的實現,和詳細商品的實現,另外還有一些附加頁面的實現,例如,關于頁面,聯系我們頁面等。關于UI層的實現,這里就不一一貼出代碼實現了,大家可以在最后的源碼鏈接自行下載查看。
## 八、系統總體架構
經過上面的所有步驟,本專題中的網上書店構建工作就基本完成了,接下來我們來看看網上書店的總體架構圖(這里架構圖直接借鑒了dax.net的圖了,因為本系列文章也是對其Byteart Retail項目的剖析過程):

最后附上整個解決方案的結構圖:

## 九、網上書店運行效果
實現完之后,大家是不是都已經迫不及待地想看到網上書店的運行效果呢?下面就為大家來揭曉,目前網上書店主要包括2個頁面,一個是商品首頁的展示和商品詳細信息的展示。首先看下商品首頁的樣子吧:

圖書的詳細信息頁面:

## 十、總結
到這里,本專題的內容就介紹完了, 本專題主要介紹面向領域驅動設計的分層架構和面向服務架構。然后結合它們在網上書店中進行實戰演練。在后面的專題中我會在該項目中一直進行完善,從而形成一個完整了DDD案例。在接下來的專題會對倉儲的實現應用規約模式,在應用之前,我會先寫一個專題來介紹規約模式來作為一個準備工作。
GitHub 開源地址:[https://github.com/lizhi5753186/OnlineStore](https://github.com/lizhi5753186/OnlineStore)。
- 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 領域驅動設計實戰系列總結