原文地址:http://www.cnblogs.com/huangxincheng/archive/2011/11/06/2238273.html
今天是速成的第三天,再分享一下WCF中比较常用的一种技术,也就是”事务“。
在B2B的项目中,一般用户注册后,就有一个属于自己的店铺,此时,我们就要插入两张表, User和Shop表。
当然,要么插入成功,要么全失败。
第一步: 首先看一下项目的结构图:
第二步: 准备工作,我们新建Commerce数据库,用EF去映射,然后新建ServiceWCF类库,具体步骤就省略,
这一块不懂可以留言。
第三步:新建一个Model类库。建立两个实体类Shop和User,当然自定义类型在WCF中传输,
必须在类上加上【DataContract】,属性上加【DataMember】。
Shop.cs
1 namespace Model 2 { 3 [DataContract] 4 public class Shop 5 { 6 [DataMember] 7 public int ShopID { get; set; } 8 9 [DataMember]10 public int UserID { get; set; }11 12 [DataMember]13 public string ShopName { get; set; }14 15 [DataMember]16 public string ShopUrl { get; set; }17 18 }19 }
User.cs
1 namespace Model 2 { 3 [DataContract] 4 public class User 5 { 6 [DataMember] 7 public int UserID { get; set; } 8 9 [DataMember]10 public string UserName { get; set; }11 12 [DataMember]13 public string Password { get; set; }14 }15 }
第四步:然后在ServiceWCF类库中新建两个文件Seller.cs 和 ISeller.cs.
ISeller.cs:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Runtime.Serialization; 5 using System.ServiceModel; 6 using System.Text; 7 8 namespace ServiceWCF 9 {10 [ServiceContract]11 public interface ISeller12 {13 [OperationContract(Name = "AddUser")]14 bool Add(Model.User user, out int userID);15 16 [OperationContract(Name = "AddShop")]17 bool Add(Model.Shop shop, out int shopID);18 19 [OperationContract]20 bool Add(Model.User user, Model.Shop shop);21 }22 }
Seller.cs
1 namespace ServiceWCF 2 { 3 public class Seller : ISeller 4 { 5 ///<summary> 6 /// User的插入操作 7 ///</summary> 8 ///<param name="user"></param> 9 ///<param name="userID"></param>10 ///<returns></returns>11 public bool Add(Model.User user, out int userID)12 {13 using (CommerceEntities db = new CommerceEntities())14 {15 try16 {17 User userModel = new User()18 {19 UserName = user.UserName,20 Passwrod = user.Password21 };22 23 db.User.AddObject(userModel);24 25 db.SaveChanges();26 27 userID = userModel.UserID;28 29 return true;30 }31 catch (Exception)32 {33 userID = 0;34 throw;35 }36 }37 }38 39 ///<summary>40 /// Shop的插入操作41 ///</summary>42 ///<param name="shop"></param>43 ///<param name="shopID"></param>44 ///<returns></returns>45 public bool Add(Model.Shop shop, out int shopID)46 {47 using (CommerceEntities db = new CommerceEntities())48 {49 try50 {51 52 Shop shopModel = new Shop()53 {54 ShopName = shop.ShopName,55 ShopUrl = shop.ShopUrl,56 UserID = shop.UserID57 };58 59 db.Shop.AddObject(shopModel);60 61 db.SaveChanges();62 63 shopID = shopModel.ShopID;64 65 return true;66 }67 catch (Exception)68 {69 shopID = 0;70 throw;71 }72 }73 }74 75 ///<summary>76 /// User,Shop的插入的操作77 ///</summary>78 ///<param name="user"></param>79 ///<param name="shop"></param>80 ///<returns></returns>
81 [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]82 public bool Add(Model.User user, Model.Shop shop)83 {84 int shopID;85 int UserID;86 87 //注意,这个方法操作了两个数据库实例,为AddUser和AddShop。所以晋升为分布式事务88 if (Add(user, out UserID))89 {90 shop.UserID = UserID;91 92 return Add(shop, out shopID);93 }94 95 return false;96 }97 }98 }
TransactionScopeRequired: 告诉ServiceHost自托管服务,进入我的方法,必须给我加上事务。
TransactionAutoComplete: 方法执行中,如果没有抛出异常,则自动提交。
第五步: 新建Host来承载了,配置AppConfig,这些细节就不再说了。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace ServiceHost 7 { 8 class Program 9 {10 static void Main(string[] args)11 {12 System.ServiceModel.ServiceHost host = new System.ServiceModel.ServiceHost(typeof(ServiceWCF.Seller));13 14 host.Open();15 16 Console.WriteLine("WCF 服务已经开启!");17 18 Console.Read();19 }20 }21 }
1 <?xml version="1.0" encoding="utf-8"?> 2 <configuration> 3 <system.web> 4 <compilation debug="true" /> 5 </system.web> 6 <!-- 部署服务库项目时,必须将配置文件的内容添加到 7 主机的 app.config 文件中。System.Configuration 不支持库的配置文件。--> 8 <system.serviceModel> 9 <services>10 <service name="ServiceWCF.Seller">11 <endpoint address="" binding="wsHttpBinding" contract="ServiceWCF.ISeller">12 <identity>13 <dns value="localhost" />14 </identity>15 </endpoint>16 <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />17 <host>18 <baseAddresses>19 <add baseAddress="http://localhost:8732/Seller/" />20 </baseAddresses>21 </host>22 </service>23 </services>24 <behaviors>25 <serviceBehaviors>26 <behavior>27 <!-- 为避免泄漏元数据信息,28 请在部署前将以下值设置为 false 并删除上面的元数据终结点 -->29 <serviceMetadata httpGetEnabled="True" />30 <!-- 要接收故障异常详细信息以进行调试,31 请将以下值设置为 true。在部署前设置为 false 32 以避免泄漏异常信息-->33 <serviceDebug includeExceptionDetailInFaults="False" />34 </behavior>35 </serviceBehaviors>36 </behaviors>37 </system.serviceModel>38 <connectionStrings>39 <add name="CommerceEntities" connectionString="metadata=res://*/Commerce.csdl|res://*/Commerce.ssdl|res://*/Commerce.msl;provider=System.Data.SqlClient;provider connection string="Data Source=VONXCEVF0IT7JDJ;Initial Catalog=Commerce;Integrated Security=True;MultipleActiveResultSets=True"" providerName="System.Data.EntityClient" />40 </connectionStrings>41 </configuration>
第六步:开启WCF服务,新建ServiceClient类库,然后用信道生成实例。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.ServiceModel; 6 using ServiceWCF; 7 8 namespace ServiceClient 9 {10 class Program11 {12 static void Main(string[] args)13 {14 var user = new Model.User()15 {16 UserName = "huangxincheng520",17 Password = "i can fly"18 };19 20 var shop = new Model.Shop()21 {22 ShopName = "shopex",23 ShopUrl = "http://www.shopex.cn"24 };25 26 var factory = new ChannelFactory<ISeller>(new WSHttpBinding(),
new EndpointAddress("http://localhost:8732/Seller/"));27 28 var client = factory.CreateChannel();29 30 if (client.Add(user, shop))31 Console.WriteLine("huangxincheng520, 恭喜你,数据插入成功。");32 else33 Console.WriteLine("huangxincheng520, 呜呜,数据插入失败。");34 35 Console.Read();36 }37 }38 }
最后就是测试了:
首先:走正常流程。client.Add方法调用服务器端,运行效果如图所示:
是的,数据已经正常插入成功,对Client端而言,这个操作是透明的。
然后: 我们在Seller类中的Add方法中故意加入异常。看效果咋样。
1 ///<summary> 2 /// User,Shop的插入的操作 3 ///</summary> 4 ///<param name="user"></param> 5 ///<param name="shop"></param> 6 ///<returns></returns> 7 [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] 8 public bool Add(Model.User user, Model.Shop shop) 9 {10 int shopID;11 int UserID;12 13 if (Add(user, out UserID))14 {15 //注意注意,我在Adduser成功的情况下,抛出异常,看是否回滚。16 throw new Exception();17 18 shop.UserID = UserID;19 20 return Add(shop, out shopID);21 }22 23 return false;24 }
截图如下:
哈哈,抛出异常了,我的Exception起到效果了,再来看一下数据库。大家都知道会发生什么了,对的,异常不再产生数据了,
还是先前产生了那条数据,说明起到效果了。