# 跟我一起学WCF(10)——WCF中事务处理
## 一、引言
好久没更新,总感觉自己欠了什么一样的,所以今天迫不及待地来更新了,因为后面还有好几个系列准备些,还有很多东西需要学习总结的。今天就来介绍下WCF对事务的支持。
## 二、WCF事务详解
## 2.1 事务概念与属性
首先,大家在学习数据库的时候就已经接触到事务这个概念了。所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单元。例如,银行转账功能,这个功能涉及两个逻辑操作
1. 从一个账户A中扣钱
2. 另一个账户B增加对应的钱。
现实生活中,这两个操作需要要么都执行,要么都不执行。所以在实现转账功能时,这两个操作就可以作为一个事务来进行提交,这样才能够保证转账功能的正确执行。
上面通过银行转账的例子来解释了事务的概念了,也可以说非常容易理解。然后在数据库的相关书籍里面都会介绍事务的特性。一个逻辑工作单元要成为事务,必须满足四个特性,这四个特性包括原子性、一致性、隔离性和持久性。这四个特性也简称为ACID(ACID是四个特性英文单词首字母的缩写)。
* 原子性。此属性可确保特定事务下完成的所有更新都已提交并保持持久,或所有这些更新都已中止并回滚到其先前状态。
* 一致性。此属性可保证某一事务下所做的更改表示从一种一致状态转换到另一种一致状态。例如,将钱从支票帐户转移到存款帐户的事务并不改变整个银行帐户中的钱的总额。
* 隔离。此属性可防止事务遵循属于其他并发事务的未提交的更改。隔离在确保一种事务不能对另一事务的执行产生意外的影响的同时,还提供一个抽象的并发。
* 持久性。这意味着一旦提交对托管资源(如数据库记录)的更新,即使出现失败这些更新也会保持持久。
## 2.2 事务协议
WCF支持分布式事务,也就是说WCF中的事务可以跨越服务边界、进程、机器和网络,在多个客户端和服务之间存在。即WCF中事务可以被传播的。既然WCF支持事务,则自然就有对应传输事务信息的相关协议。所以也就有了事务协议。
WCF使用不同的事务协议来控制事务的执行范围,事务协议是为了实现分布式环境中事务的传播。WCF支持以下三种事务协议:
1. 轻量级协议(Lightweight Protocol):该协议仅用于管理本地环境中的事务,即处于同一应用程序域中的事务。它无法跨越应用程序域的边界传播事务,则更不用说跨越进程和机器边界了。所以Lightweight Protocol只适用于某个服务的内部或外部。但相对于其他协议来说,轻量级协议的性能是最好的,这应该是显然的,不能跨越进程和机器边界,则就不存在网络传输。
2. OleTx协议:该协议用于跨应用程序域、进程和机器边界传播事务。协议采用远程过程调用(RPC),并采用Windows专用的二进制格式。但该协议无法穿越防火墙或与非Windows方协作。所以OleTx协议多用于Windows体系下的内网环境(即Intranet环境)。
3. WS-Atomic(WS原子性,WSAT)事务协议:该协议与OleTx协议类似,同样允许事务穿越应用程序域、进程和机器边界传播事务。但不同于OleTx协议的是,WSAT协议基于一种行业标准,它使用HTTP协议,并编码形式为文本格式,因而可以穿越防火墙。虽然可以在内网中使用WSAT协议,但它主要还是用于Internet环境。
因为轻量级协议不能跨越服务边界传播事务,所有没有绑定支持轻量级协议。WCF预定义的绑定中实现了标准的WS-Atomic 协议和Microsoft专有的OleTx协议,我们可以通过编程或配置文件来设置事务协议。具体设置方法如下所示:
```
1 <bindings>
2 <netTcpBinding>
3 <!--通过transactionProtocol属性来设置事务协议-->
4 <binding name="transactionTCP" transactionFlow="true" transactionProtocol="WSAtomicTransactionOctober2004"/>
5 </netTcpBinding>
6 </bindings>
7 // 通过编程设置
8 NetTcpBinding tcpBinding = new NetTcpBinding();
9 // 注意: 事务协议的配置只有在事务传播的情况下才有意义
10 tcpBinding.TransactionFlow = true;
11 tcpBinding.TransactionProtocol = TransactionProtocol.WSAtomicTransactionOctober2004;
```
这里需要注意,事务协议的配置只有在允许事务传播的情况下才有意义。并且NetTcpBinding和NetNamedPipeBinding都提供了TransactionProtocol属性。由于TCP和IPC绑定只能在内网使用,将它们设置为WSAT协议并无实际意义,对于WS绑定(如WSHttpBinding、WSDualHttpBinding和WSFederationHttpBinding)并没有TransactionProtocol属性,它们设计的目的在于当涉及多个使用WAST协议的事务管理器时,能够跨越Internet。但如果只有一个事务协调器,OleTx协议将是默认的协议,不必也不能为它配置一个特殊的协议。
## 2.3 事务管理器
分布式事务的实现要依靠第三方事务管理器来实现。它负责管理一个个事务的执行情况,最后根据全部事务的执行结果,决定提交或回滚整个事务。WCF提供了三个不同的事务管理器,它们分别是轻量级事务管理器(LTM)、核心事务管理器(KTM)和分布式事务协调器(DTC)。WCF根据平台使用的公共,应用程序的事务执行的任务、调用的服务以及所消耗的资源分配合适的事务管理器。通过自动地分配事务管理器,WCF将事务管理从服务代码和用到的事务协议中解耦出来,开发者不必为事务管理器而苦恼。下面分别介绍下这三种事务管理器。
* LTM:它只管理本地事务,即在一个单独应用程序域内的事务。LTM只能管理在一个单独服务内的事务,该服务不能将事务传递给其他服务。LTM在所有的事务管理器中,性能最好。
* KTM:与LTM一样,KTM管理的事务只能引入一个服务,并且该服务不得向其他服务传播事务。
* DTC:DTC可以管理跨越任意执行边界的事务,从本地跨越所有的边界,如进程、机器或站点的边界。DTC既可以使用OleTx协议,也可以使用WSAT协议。DTC与WCF紧密的集成一起,它是每个运行WCF的机器上默认可用的系统服务,DTC可以创建新的事务、跨机器传播事务,手机之一管理器的投票并通知资源管理器进行回滚或提交。
## 2.4 服务支持的4种事务模式
事务使用哪个事务由绑定的事务流属性([TransactionFlow](http://msdn.microsoft.com/zh-cn/library/system.servicemodel.nettcpbinding.transactionflow(v=vs.110).aspx)属性)、操作契约中的事务流选项([TransactionFlowOption](http://msdn.microsoft.com/zh-cn/library/system.servicemodel.transactionflowoption(v=vs.110).aspx)) 以及操作行为特性中的事务范围属性([TransactionScopeRequired](http://msdn.microsoft.com/zh-cn/library/system.servicemodel.operationbehaviorattribute.transactionscoperequired(v=vs.110).aspx))共同决定。TransactionFlow属性有2个值,true 或false,TransactionFlowOption有三个值,NotAllowed、Allowed和Mandatory,TransactionScopeRequired有两个值,true或false。所以一共有12种(2*3*2)可能的配置设置。在这些配置设置中,有4种不满足要求的,例如在绑定中设置TransactionFlow属性为false,却设置TransactionFlowOption为Mandatory。下图列出了剩下的8种情况:
![](https://box.kancloud.cn/2016-01-23_56a2eb49694ba.png)
上图中的8中排列组合实际最终只产生了四种事务传播模式,这4种传播模式为:Client/Service、Client、Service和None。上图黑体字指出各种模式推荐的配置设置。在设计应用程序时,每种模式都有它自己的适用场景。对于除None模式的其他三种模式的推荐配置详细介绍如下所示:
* Client/Service:最常见的一种事务模型,通常由客户端或服务本身启用一个事务。设置步骤:
(1) 选择一个支持事务的Binding,设置 TransactionFlow = true。 (2) 设置 TransactionFlow(TransactionFlowOption.Allowed)。 (3) 设置 OperationBehavior(TransactionScopeRequired=true)。
* Client:强制服务必须参与事务,而且必须是客户端启用事务。设置步骤:
(1) 选择一个支持事务的Binding,设置 TransactionFlow = true。 (2) 设置 TransactionFlow(TransactionFlowOption.Mandatory)。 (3) 设置 OperationBehavior(TransactionScopeRequired=true)。
* Service:服务必须启用一个根事务,且不参与任何外部事务。设置步骤:
(1) 选择任何一种Binding,设置 TransactionFlow = false(默认)。 (2) 设置 TransactionFlow(TransactionFlowOption.NotAllowed)。 (3) 设置 OperationBehavior(TransactionScopeRequired=true)。
## 三、WCF事务服务的实现
上面内容对WCF中事务进行了一个详细的介绍,下面具体通过一个实例来说明WCF中如何实现对事务的支持。首先还是按照前面博文中介绍的步骤来实现该实例。
第一步:创建WCF契约和契约的实现,具体的实现代码如下所示:
```
namespace WCFContractAndService
{
// 服务契约
[ServiceContract(SessionMode= SessionMode.Required)]
//[ServiceBehavior(ReleaseServiceInstanceOnTransactionComplete = false)]
public interface IOrderService
{
// 操作契约
[OperationContract]
// 控制客户端的事务是否传播到服务
// TransactionFlow的值会包含在服务发布的元数据上
[TransactionFlow(TransactionFlowOption.NotAllowed)]
List<Customer> GetCustomers();
[OperationContract]
[TransactionFlow(TransactionFlowOption.NotAllowed)]
List<Product> GetProducts();
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
string PlaceOrder(Order order);
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
string AdjustInventory(int productId, int quantity);
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
string AdjustBalance(int customerId, decimal amount);
}
[DataContract]
public class Customer
{
[DataMember]
public int CustomerId { get; set; }
[DataMember]
public string CompanyName { get; set; }
[DataMember]
public decimal Balance { get; set; }
}
[DataContract]
public class Product
{
[DataMember]
public int ProductId { get; set; }
[DataMember]
public string ProductName { get; set; }
[DataMember]
public decimal Price { get; set; }
[DataMember]
public int OnHand { get; set; }
}
[DataContract]
public class Order
{
[DataMember]
public int CustomerId { get; set; }
[DataMember]
public int ProductId { get; set; }
[DataMember]
public decimal Price { get; set; }
[DataMember]
public int Quantity { get; set; }
[DataMember]
public decimal Amount { get; set; }
}
}
namespace WCFContractAndService
{
// 服务实现
[ServiceBehavior(
TransactionIsolationLevel = IsolationLevel.Serializable,
TransactionTimeout= "00:00:30",
InstanceContextMode = InstanceContextMode.PerSession,
TransactionAutoCompleteOnSessionClose = true)]
public class OrderService :IOrderService
{
private List<Customer> customers = null;
private List<Product> products = null;
private int orderId = 0;
private string conString = Properties.Settings.Default.TransactionsConnectionString;
public List<Customer> GetCustomers()
{
customers = new List<Customer>();
using (var cnn = new SqlConnection(conString))
{
using (var cmd = new SqlCommand("SELECT * " + "FROM Customers ORDER BY CustomerId", cnn))
{
cnn.Open();
using (SqlDataReader CustomersReader = cmd.ExecuteReader())
{
while (CustomersReader.Read())
{
var customer = new Customer();
customer.CustomerId = CustomersReader.GetInt32(0);
customer.CompanyName = CustomersReader.GetString(1);
customer.Balance = CustomersReader.GetDecimal(2);
customers.Add(customer);
}
}
}
}
return customers;
}
public List<Product> GetProducts()
{
products = new List<Product>();
using (var cnn = new SqlConnection(conString))
{
using (var cmd = new SqlCommand(
"SELECT * " +
"FROM Products ORDER BY ProductId", cnn))
{
cnn.Open();
using (SqlDataReader productsReader =
cmd.ExecuteReader())
{
while (productsReader.Read())
{
var product = new Product();
product.ProductId = productsReader.GetInt32(0);
product.ProductName = productsReader.GetString(1);
product.Price = productsReader.GetDecimal(2);
product.OnHand = productsReader.GetInt16(3);
products.Add(product);
}
}
}
}
return products;
}
// 设置服务的环境事务
// 使用Client模式,即使用客户端的事务
[OperationBehavior(TransactionScopeRequired =true, TransactionAutoComplete = false)]
public string PlaceOrder(Order order)
{
using (var conn = new SqlConnection(conString))
{
var cmd = new SqlCommand(
"Insert Orders (CustomerId, ProductId, " +
"Quantity, Price, Amount) " + "Values( " +
"@customerId, @productId, @quantity, " +
"@price, @amount)", conn);
cmd.Parameters.Add(new SqlParameter(
"@customerId", order.CustomerId));
cmd.Parameters.Add(new SqlParameter(
"@productid", order.ProductId));
cmd.Parameters.Add(new SqlParameter(
"@price", order.Price));
cmd.Parameters.Add(new SqlParameter(
"@quantity", order.Quantity));
cmd.Parameters.Add(new SqlParameter(
"@amount", order.Amount));
try
{
conn.Open();
if (cmd.ExecuteNonQuery() <= 0)
{
return "The order was not placed";
}
cmd = new SqlCommand(
"Select Max(OrderId) From Orders " +
"Where CustomerId = @customerId", conn);
cmd.Parameters.Add(new SqlParameter(
"@customerId", order.CustomerId));
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
orderId = Convert.ToInt32(reader[0].ToString());
}
}
return string.Format("Order {0} was placed", orderId);
}
catch (Exception ex)
{
throw new FaultException(ex.Message);
}
}
}
// 使用Client模式,即使用客户端的事务
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
public string AdjustInventory(int productId, int quantity)
{
using (var conn = new SqlConnection(conString))
{
var cmd = new SqlCommand(
"Update Products Set OnHand = " +
"OnHand - @quantity " +
"Where ProductId = @productId", conn);
cmd.Parameters.Add(new SqlParameter(
"@quantity", quantity));
cmd.Parameters.Add(new SqlParameter(
"@productid", productId));
try
{
conn.Open();
if (cmd.ExecuteNonQuery() <= 0)
{
return "The inventory was not updated";
}
else
{
return "The inventory was updated";
}
}
catch (Exception ex)
{
throw new FaultException(ex.Message);
}
}
}
// 使用Client模式,即使用客户端的事务
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
public string AdjustBalance(int customerId, decimal amount)
{
using (var conn = new SqlConnection(conString))
{
var cmd = new SqlCommand(
"Update Customers Set Balance = " +
"Balance - @amount " +
"Where CustomerId = @customerId", conn);
cmd.Parameters.Add(new SqlParameter(
"@amount", amount));
cmd.Parameters.Add(new SqlParameter(
"@customerId", customerId));
try
{
conn.Open();
if (cmd.ExecuteNonQuery() <= 0)
{
return "The balance was not updated";
}
else
{
return "The balance was updated";
}
}
catch (Exception ex)
{
throw new FaultException(ex.Message);
}
}
}
}
}
```
上面的服务契约和服务实现与传统的实现没什么区别。这里使用IIS来宿主WCF服务。
第二步:宿主的实现。创建一个空的Web的项目,并添加WCF服务文件,具体内容如下所示:
对应的Web.config的内容如下所示:
```
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="OrderServiceBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<!--通过设置transactionFlow属性为true来使绑定支持事务传播;对于wsHttpBinding契约事务传播-->
<binding name="wsHttpBinding" transactionFlow="true">
<!--启用消息可靠性选项-->
<!--<reliableSession enabled="true"/>-->
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="WCFContractAndService.OrderService" behaviorConfiguration="OrderServiceBehavior">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpBinding" contract="WCFContractAndService.IOrderService"/>
</service>
</services>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
</system.serviceModel>
</configuration>
```
这里采用了wsHttpBinding绑定,并设置其transactionFlow属性为true使其支持事务传播。接下来看看客户端的实现。
第三步:WCF客户端的实现,通过添加服务引用的方式来生成代理类。这里的客户端是WinForm程序。
```
1 public partial class Form1 : Form
2 {
3 public Form1()
4 {
5 InitializeComponent();
6 }
7
8 private Customer customer = null;
9 private List<Customer> customers = null;
10 private Product product = null;
11 private List<Product> products = null;
12 private OrderServiceClient proxy = null;
13 private Order order = null;
14 private string result = String.Empty;
15
16 private void Form1_Load(object sender, EventArgs e)
17 {
18 proxy = new OrderServiceClient("WSHttpBinding_IOrderService");
19 GetCustomersAndProducts();
20 }
21
22 private void GetCustomersAndProducts()
23 {
24 customers = proxy.GetCustomers().ToList<Customer>();
25 customerBindingSource.DataSource = customers;
26
27 products = proxy.GetProducts().ToList<Product>();
28 productBindingSource.DataSource = products;
29 }
30
31 private void placeOrderButton_Click(object sender, EventArgs e)
32 {
33 customer = (Customer)this.customerBindingSource.Current;
34 product = (Product)this.productBindingSource.Current;
35 Int32 quantity = Convert.ToInt32(quantityTextBox.Text);
36
37 order = new Order();
38 order.CustomerId = customer.CustomerId;
39 order.ProductId = product.ProductId;
40 order.Price = product.Price;
41 order.Quantity = quantity;
42 order.Amount = order.Price * Convert.ToDecimal(order.Quantity);
43
44 // 事务处理
45 using (var tranScope = new TransactionScope())
46 {
47 proxy = new OrderServiceClient("WSHttpBinding_IOrderService");
48 {
49 try
50 {
51 result = proxy.PlaceOrder(order);
52 MessageBox.Show(result);
53
54 result = proxy.AdjustInventory(product.ProductId, quantity);
55 MessageBox.Show(result);
56
57 result = proxy.AdjustBalance(customer.CustomerId,
58 Convert.ToDecimal(quantity) * order.Price);
59 MessageBox.Show(result);
60
61 proxy.Close();
62 tranScope.Complete(); // Cmmmit transaction
63 }
64 catch (FaultException faultEx)
65 {
66 MessageBox.Show(faultEx.Message +
67 "\n\nThe order was not placed");
68
69 }
70 catch (ProtocolException protocolEx)
71 {
72 MessageBox.Show(protocolEx.Message +
73 "\n\nThe order was not placed");
74 }
75 }
76 }
77
78 // 成功提交后强制刷新界面
79 quantityTextBox.Clear();
80 try
81 {
82 proxy = new OrderServiceClient("WSHttpBinding_IOrderService");
83 GetCustomersAndProducts();
84 }
85 catch (FaultException faultEx)
86 {
87 MessageBox.Show(faultEx.Message);
88 }
89 }
90 }
```
从上面代码可以看出,WCF事务的实现是利用[TransactionScope](http://msdn.microsoft.com/zh-cn/library/system.transactions.transactionscope(v=vs.110).aspx)事务类来完成的。下面让我们看看程序的运行结果。在运行程序之前,我们必须运行SQL脚本来创建程序中的使用的数据库,具体的脚本如下所示:
```
1 USE [TransactionsDemo]
2 GO
3 /****** Object: Table [dbo].[Customers] Script Date: 01/15/2009 08:14:25 ******/
4 SET ANSI_NULLS ON
5 GO
6 SET QUOTED_IDENTIFIER ON
7 GO
8 CREATE TABLE [dbo].[Customers](
9 [CustomerId] [int] IDENTITY(1,1) NOT NULL,
10 [Name] [nvarchar](20) NOT NULL,
11 [Balance] [smallmoney] NOT NULL, check(Balance >= 0),
12 CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED
13 (
14 [CustomerId] ASC
15 )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
16 ) ON [PRIMARY]
17 GO
18 /****** Object: Table [dbo].[Products] Script Date: 01/15/2009 08:14:25 ******/
19 SET ANSI_NULLS ON
20 GO
21 SET QUOTED_IDENTIFIER ON
22 GO
23 CREATE TABLE [dbo].[Products](
24 [ProductId] [int] IDENTITY(1,1) NOT NULL,
25 [Name] [nvarchar](20) NOT NULL,
26 [Price] [smallmoney] NOT NULL,
27 [OnHand] [smallint] NOT NULL, check(OnHand >= 0),
28 CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED
29 (
30 [ProductId] ASC
31 )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
32 ) ON [PRIMARY]
33 GO
34 /****** Object: Table [dbo].[Orders] Script Date: 01/15/2009 08:14:25 ******/
35 SET ANSI_NULLS ON
36 GO
37 SET QUOTED_IDENTIFIER ON
38 GO
39 CREATE TABLE [dbo].[Orders](
40 [OrderId] [int] IDENTITY(1,1) NOT NULL,
41 [CustomerId] [int] NOT NULL,
42 [ProductId] [int] NOT NULL,
43 [Quantity] [smallint] NOT NULL,
44 [Price] [smallmoney] NOT NULL,
45 [Amount] [smallmoney] NOT NULL,
46 CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED
47 (
48 [OrderId] ASC
49 )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
50 ) ON [PRIMARY]
51 GO
52 INSERT Customers (Name, Balance) VALUES ('Contoso', 10000)
53 INSERT Customers (Name, Balance) VALUES ('Northwind', 25000)
54 INSERT Customers (Name, Balance) VALUES ('Litware', 50000)
55 INSERT Products (Name, Price, OnHand) VALUES ('Wood', 100, 1000)
56 INSERT Products (Name, Price, OnHand) VALUES ('Wallboard', 200, 2500)
57 INSERT Products (Name, Price, OnHand) VALUES ('Pipe', 500, 5000)
58 GO
```
生成程序使用的数据库之后,按F5运行WCF客户端程序,并在出现的界面中购买Wood材料100,运行结果如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb4981e10.png)
单击Place order按钮后,即执行下订单操作,如果订单成功后,将会更新产品的库存和用户的余额,你将看到如下图所示的运行结果:
![](https://box.kancloud.cn/2016-01-23_56a2eb499051a.png)
## 四、小结
到这里,关于WCF中事务的介绍就结束了。WCF支持四种事务模式,Client/Service、Client、Service和None,对于每种模式都有其不同的配置。一般尽量使用Client/Service或Client事务模式。WCF事务的实现借助于已有的System.Transaction实现本地事务的编程,而分布式事务则借助MSDTC分布式事务协调机制来实现。WCF提供了支持事务传播的绑定协议包括:wsHttpBinding、WSDualHttpBinding、WSFederationBinding、NetTcpBinding和NetNamedPipeBinding,最后两个绑定允许选择WS-AT协议或OleTx协议,而其他绑定都使用标准的WS-AT协议。在一一篇博文将分享WCF对消息队列的支持。
本文所有源代码:[WCFTransaction.zip](http://files.cnblogs.com/zhili/WCFTransaction.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 领域驱动设计实战系列总结