当然,要么插入成功,要么全失败。
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没有新增)