WCF之分布式事务一

    在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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值