wcf系列学习5天速成——第三天 事务的使用

简介:

今天是速成的第三天,再分享一下WCF中比较常用的一种技术,也就是”事务“。

 

在B2B的项目中,一般用户注册后,就有一个属于自己的店铺,此时,我们就要插入两张表, User和Shop表。

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

 

第一步: 首先看一下项目的结构图:

 

第二步: 准备工作,我们新建Commerce数据库,用EF去映射,然后新建ServiceWCF类库,具体步骤就省略,

            这一块不懂可以留言。

 

第三步:新建一个Model类库。建立两个实体类Shop和User,当然自定义类型在WCF中传输,

           必须在类上加上【DataContract】,属性上加【DataMember】。

   Shop.cs

namespace Model
{
    [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; }

    }
}

 User.cs
namespace Model
{
    [DataContract]
    public class User
    {
        [DataMember]
        public int UserID { get; set; }

        [DataMember]
        public string UserName { get; set; }

        [DataMember]
        public string Password { get; set; }
    }
}

 

第四步:然后在ServiceWCF类库中新建两个文件Seller.cs 和 ISeller.cs.

        ISeller.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace ServiceWCF
{
    [ServiceContract]
    public interface ISeller
    {
        [OperationContract(Name = "AddUser")]
        bool Add(Model.User user, out int userID);

        [OperationContract(Name = "AddShop")]
        bool Add(Model.Shop shop, out int shopID);

        [OperationContract]
        bool Add(Model.User user, Model.Shop shop);
    }
}

     Seller.cs
namespace ServiceWCF
{
    public class Seller : ISeller
    {
        ///<summary>
/// User的插入操作
///</summary>
///<param name="user"></param>
///<param name="userID"></param>
///<returns></returns>
        public bool Add(Model.User user, out int userID)
        {
            using (CommerceEntities db = new CommerceEntities())
            {
                try
                {
                    User userModel = new User()
                    {
                        UserName = user.UserName,
                        Passwrod = user.Password
                    };

                    db.User.AddObject(userModel);

                    db.SaveChanges();

                    userID = userModel.UserID;

                    return true;
                }
                catch (Exception)
                {
                    userID = 0;
                    throw;
                }
            }
        }

        ///<summary>
/// Shop的插入操作
///</summary>
///<param name="shop"></param>
///<param name="shopID"></param>
///<returns></returns>
        public bool Add(Model.Shop shop, out int shopID)
        {
            using (CommerceEntities db = new CommerceEntities())
            {
                try
                {

                    Shop shopModel = new Shop()
                          {
                              ShopName = shop.ShopName,
                              ShopUrl = shop.ShopUrl,
                              UserID = shop.UserID
                          };

                    db.Shop.AddObject(shopModel);

                    db.SaveChanges();

                    shopID = shopModel.ShopID;

                    return true;
                }
                catch (Exception)
                {
                    shopID = 0;
                    throw;
                }
            }
        }

///<summary>
/// User,Shop的插入的操作
///</summary>
///<param name="user"></param>
///<param name="shop"></param>
///<returns></returns>
        [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
        public bool Add(Model.User user, Model.Shop shop)
        {
            int shopID;
            int UserID;

            //注意,这个方法操作了两个数据库实例,为AddUser和AddShop。所以晋升为分布式事务
            if (Add(user, out  UserID))
            {
                shop.UserID = UserID;

                return Add(shop, out shopID);
            }

            return false;
        }
    }
}

 TransactionScopeRequired: 告诉ServiceHost自托管服务,进入我的方法,必须给我加上事务。

 TransactionAutoComplete:   方法执行中,如果没有抛出异常,则自动提交。

 

第五步: 新建Host来承载了,配置AppConfig,这些细节就不再说了。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ServiceHost
{
    class Program
    {
        static void Main(string[] args)
        {
            System.ServiceModel.ServiceHost host = new System.ServiceModel.ServiceHost(typeof(ServiceWCF.Seller));

            host.Open();

            Console.WriteLine("WCF 服务已经开启!");

            Console.Read();
        }
    }
}


<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <!-- 部署服务库项目时,必须将配置文件的内容添加到 
  主机的 app.config 文件中。System.Configuration 不支持库的配置文件。-->
  <system.serviceModel>
    <services>
      <service name="ServiceWCF.Seller">
        <endpoint address="" binding="wsHttpBinding" contract="ServiceWCF.ISeller">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8732/Seller/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- 为避免泄漏元数据信息,
          请在部署前将以下值设置为 false 并删除上面的元数据终结点  -->
          <serviceMetadata httpGetEnabled="True" />
          <!-- 要接收故障异常详细信息以进行调试,
          请将以下值设置为 true。在部署前设置为 false 
            以避免泄漏异常信息-->
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
  <connectionStrings>
    <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" />
  </connectionStrings>
</configuration>


第六步:开启WCF服务,新建ServiceClient类库,然后用信道生成实例。

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using ServiceWCF;

namespace ServiceClient
{
    class Program
    {
        static void Main(string[] args)
        {
            var user = new Model.User()
            {
                UserName = "huangxincheng520",
                Password = "i can fly"
            };

            var shop = new Model.Shop()
            {
                ShopName = "shopex",
                ShopUrl = "http://www.shopex.cn"
            };

            var factory = new ChannelFactory<ISeller>(new WSHttpBinding(),
                                  new EndpointAddress("http://localhost:8732/Seller/"));

            var client = factory.CreateChannel();

            if (client.Add(user, shop))
                Console.WriteLine("huangxincheng520, 恭喜你,数据插入成功。");
            else
                Console.WriteLine("huangxincheng520, 呜呜,数据插入失败。");

            Console.Read();
        }
    }
}

最后就是测试了:

    首先:走正常流程。client.Add方法调用服务器端,运行效果如图所示:

  

 

是的,数据已经正常插入成功,对Client端而言,这个操作是透明的。

  

  然后:  我们在Seller类中的Add方法中故意加入异常。看效果咋样。

///<summary>
/// User,Shop的插入的操作
///</summary>
///<param name="user"></param>
///<param name="shop"></param>
///<returns></returns>
        [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
        public bool Add(Model.User user, Model.Shop shop)
        {
            int shopID;
            int UserID;
            
            if (Add(user, out  UserID))
            {
                //注意注意,我在Adduser成功的情况下,抛出异常,看是否回滚。
                throw new Exception();

                shop.UserID = UserID;

                return Add(shop, out shopID);
            }

            return false;
        }

 

截图如下:

 

哈哈,抛出异常了,我的Exception起到效果了,再来看一下数据库。大家都知道会发生什么了,对的,异常不再产生数据了,

        还是先前产生了那条数据,说明起到效果了。

 

相关文章
|
9月前
|
网络协议 网络架构 Windows
框架学习——WCF框架
框架学习——WCF框架
229 0
|
安全 C#
WCF技术我们应该如何以正确的方式去学习掌握
一、WCF技术我该如何学习?       阿笨的回答是:作为初学者的我们,那么请跟着阿笨一起玩WCF吧,阿笨将带领大家如何以正确的姿势去掌握WCF技术。由于WCF技术知识点太多了,就纯基础概念性知识都可以单独出一本书来讲解,本次分享课程《C#面向服务编程技术WCF从入门到实战演练》开课之前,阿笨还是希望从没了解过WCF技术的童鞋们提前先了解一下WCF技术,至少要明白WCF技术的ABC三要素分别指的是什么。
1171 0
跟着Artech学习WCF扩展(1) Binding进行通信
这个demo简单 就一个服务器段的一个客户端的 主要是注释 Server的 using System; using System.Collections.Generic; using System.
689 0
跟着Artech学习WCF扩展(2) 自定义Channel与执行的顺序
源代码下载地址:点我 原文地址:http://www.cnblogs.com/artech/archive/2008/07/09/1238626.html 这节不看源码 看着真费劲哈   服务器端是这样的顺序 MyBindingElement.
691 0
跟着Artech学习WCF扩展(3) 扩展CallContextInitializer
代码下载:点我   作为备份 Artech的代码要在他的博客里面找的 Artech大师级的“总分总”式的风格,的确是和高手过招用,作为一面菜鸟,纠结了老半天才明白,原来写博客也可以前后呼应 于是把这个小节点整理到一起已被以后不时只需     5 、自定义CallContextInitializer (Step 12 & Step 18) 提到CallContextInitializer,我想有一部分人会马上想到System.Runtime.Remoting.Messaging.CallContext。
742 0