WCF之分布式事务一

来源:互联网 发布:网络词 抓马什么梗 编辑:程序博客网 时间:2024/05/21 13:58
    在B2B的项目中,一般用户注册后,就有一个属于自己的店铺,此时,我们就要插入两张表, User和Shop表。

当然,要么插入成功,要么全失败。

WCF的各种binding中,除了BasicHttpBinding,BasicHttpContextBinding,NetPeerTcpBinding之外,其他的binding都支持事务。

首先看一下项目结构图:


一、建立WCFModel

    新建一个Model类库。建立两个实体类Shop和User,当然自定义类型在WCF中传输,
必须在类上加上【DataContract】,属性上加【DataMember】。

using System;using System.Collections.Generic;using System.Runtime.Serialization;namespace WCFModel{    [DataContract]    public class User    {        [DataMember]        public int UserID { get; set; }        [DataMember]        public String UserName { get; set; }        [DataMember]        public string Password { get; set; }        [DataMember]        public ICollection<Shop> Shop { get; set; }    }}
using System.Collections.Generic;using System.ComponentModel.DataAnnotations.Schema;using System.Runtime.Serialization;namespace WCFModel{    [DataContract]    public class Shop    {        [DataMember]        public int ShopID { get; set; }        [DataMember]        public int UserID { get; set; }        [DataMember]        public string ShopName { get; set; }        [DataMember]        public string ShopUrl { get; set; }        [DataMember]        [ForeignKey("UserID")]        public ICollection<User> Users { get; set; }    }}

二、创建WCFService,并使用code first 创建数据库:

创建WCFService类库,通过nuget引用Entityframework包,并创建一个CommerceDBContext类,继承自DbContext:

using System.Data.Entity;using WCFModel;namespace WCFService{    public class CommerceDBContext:DbContext    {        public CommerceDBContext(string connectionString):base(connectionString) {            new DropCreateDatabaseIfModelChanges<CommerceDBContext>().InitializeDatabase(this);        }        public CommerceDBContext():base("CommerceDBConnectionString") {            Database.SetInitializer<CommerceDBContext>(new DropCreateDatabaseIfModelChanges<CommerceDBContext>());        }        protected override void OnModelCreating(DbModelBuilder modelBuilder)        {                    }        public IDbSet<User> Users { get; set; }        public IDbSet<Shop> Shops { get; set; }    }}

创建ISeller和Seller:

using System.ServiceModel;using WCFModel;namespace WCFService{    [ServiceContract]    public interface ISeller    {        [OperationContract(Name ="AddUser")]        bool Add(User user, out int userID);        [OperationContract(Name ="AddShop")]        bool Add(Shop shop, out int shopID);        [OperationContract]        [TransactionFlow(TransactionFlowOption.Allowed)] //This means a transaction is allowed to be propagated from the client to this operation        bool Add(User user, Shop shop);    }}
using System;using System.ServiceModel;using WCFModel;namespace WCFService{    public class Seller : ISeller    {        [OperationBehavior(TransactionScopeRequired =true,TransactionAutoComplete =true)]        public bool Add(User user, Shop shop)        {            int shopID;            int userID;            if (Add(user,out userID))            {                shop.UserID = userID;                return Add(shop, out shopID);            }            return false;        }        public bool Add(Shop shop, out int shopID)        {            using (CommerceDBContext db = new CommerceDBContext())            {                try                {                    Shop shopModel = new Shop()                    {                        ShopName = shop.ShopName,                        ShopUrl = shop.ShopUrl,                        UserID = shop.UserID                    };                    var result=db.Shops.Add(shopModel);                    db.SaveChanges();                    shopID = result.ShopID;                    return true;                }                catch (Exception)                {                    shopID = 0;                    throw;                }            }        }        public bool Add(User user, out int userID)        {            using (CommerceDBContext db = new CommerceDBContext())            {                try                {                    User userModel = new User()                    {                        UserName = user.UserName,                        Password = user.Password                    };                    var result = db.Users.Add(userModel);                    db.SaveChanges();                    userID = result.UserID;                    return true;                }                catch (Exception)                {                    userID = 0;                    throw;                }            }        }    }}
TransactionScopeRequired: 告诉ServiceHost自托管服务,进入我的方法,必须给我加上事务。
TransactionAutoComplete: 方法执行中,如果没有抛出异常,则自动提交。


三、创建WCF宿主WCFHost,并配置app.config

using System;using System.ServiceModel;using WCFService;namespace WCFHost{    class Program    {        static void Main(string[] args)        {            ServiceHost host = new ServiceHost(typeof(Seller));            host.Open();            Console.WriteLine("wcf 服务已经启动!");            Console.ReadKey();        }    }}
<?xml version="1.0" encoding="utf-8"?><configuration>  <configSections>    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />  </configSections>  <system.serviceModel>    <services>      <service name="WCFService.Seller">        <host>          <baseAddresses>            <add baseAddress="http://localhost:8733/Design_Time_Addresses/WCFService" />          </baseAddresses>        </host>        <endpoint address="" binding="wsHttpBinding" contract="WCFService.ISeller"  bindingConfiguration="transactionalWsHttpBinding">          <identity>            <dns value="localhost" />          </identity>        </endpoint>      </service>    </services>    <bindings>        <wsHttpBinding>             <binding name="transactionalWsHttpBinding"                           transactionFlow="true"                 receiveTimeout="00:10:00"                          sendTimeout="00:10:00"                 openTimeout="00:10:00"                           closeTimeout="00:10:00" />        </wsHttpBinding>     </bindings>    <behaviors>      <serviceBehaviors>        <behavior>          <serviceMetadata httpGetEnabled="true" />          <serviceDebug includeExceptionDetailInFaults="true" />        </behavior>      </serviceBehaviors>    </behaviors>  </system.serviceModel>  <entityFramework>    <providers>      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />    </providers>    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">      <parameters>        <parameter value="mssqllocaldb" />      </parameters>    </defaultConnectionFactory>  </entityFramework>  <connectionStrings>    <add name="CommerceDBConnectionString" connectionString="Server=LONGXI;Database=Commerce;UId=sa;pwd=!1@2#3qaz;" providerName="System.Data.SqlClient" />  </connectionStrings></configuration>
四、创建客户端程序调用wcf服务(用信道生成实例):

using System;using System.ServiceModel;using WCFModel;using WCFService;namespace WCFClient{    class Program    {        static void Main(string[] args)        {            var factory = new ChannelFactory<ISeller>(new WSHttpBinding(), new EndpointAddress("http://localhost:8733/Design_Time_Addresses/WCFService"));            var client = factory.CreateChannel();            var user = new User            {                UserName = "zhulongxi1",                Password = "1234"            };            var shop = new Shop            {                ShopName = "Shop1",                ShopUrl = "http://wwww.shopex.com"            };            if(client.Add(user,shop))                Console.WriteLine("插入成功!");            else                Console.WriteLine("插入失败!");            Console.ReadKey();        }    }}

五、配置Microsoft Distributed Transaction Coordinator(MSDTC)和防火墙

win+r 输入dcomcnfg.exe打开Component Services

打开防火墙的Allow a program or feature through Windows Firewall window,


六、测试:

首先走正常流程(使用事务):
先启动WCFHost:

再运行客户端程序WCFClient:插入成功

然后在Seller类中的Add方法中故意加入异常:

运行客户端程序:

抛出异常了,数据库里面没有新增数据,说明事务起到效果了。(可以把事务去掉,然后再执行,则user 新增了一条,而shop没有新增)

0 0
原创粉丝点击