数据一致性在工作中显得非常重要,有时候我们库中出现脏数据导致程序报错,但是又很难发现这样的错误,所以为了数据的完整性建议在程序中加入事物。
什么是事物:我们都有团队合作吧,比喻团队有3个人,a负责设计,b负责前端,c负责后台,那么他们三个就是一个整体,哪一个人那里出了问题就要被打回。
第一步:我们开始定义个一个接口
[ServiceContract] public interface IUserInfo { [OperationContract] int AddInfo(); }
第二步当然是实现接口了。这个AddInfo需要添加用户和文章
//使用隐式事务,并把TransactionFlowOption设置为Allowed打开事务流 [OperationBehavior(TransactionAutoComplete = true, TransactionScopeRequired = true)] [TransactionFlow(TransactionFlowOption.Allowed)] public int AddInfo() { using (TransactionScope transcope = new TransactionScope(TransactionScopeOption.RequiresNew)) { try { if (AddUser()) { if (!AddArticle()) { Transaction.Current.Rollback(); return 0; } else { transcope.Complete(); return 1; } } else { Transaction.Current.Rollback(); } } catch (Exception ep) { Transaction.Current.Rollback(); return 0; } return 0; } } /// <summary> /// 添加用户 /// </summary> /// <returns></returns> public bool AddUser() { try { string guid = System.Guid.NewGuid().ToString(); string userName = "zhangsan"; string realName = "张三"; DateTime dateTime = DateTime.Now; string sql = "insert into MyUser(Id,UserName,RealName,SysDate)values(@Id,@UserName,@RealName,@SysDate)"; SqlParameter[] param = new SqlParameter[] { new SqlParameter("@Id",guid), new SqlParameter("@UserName",userName), new SqlParameter("@RealName",realName), new SqlParameter("@SysDate",dateTime) }; string Conn = ConfigurationManager.ConnectionStrings["dbLink"].ConnectionString; return SqlHelper.ExecuteNonQuery(Conn, CommandType.Text, sql, param) > 0; } catch (Exception) { return false; } } /// <summary> /// 文献信息 /// </summary> /// <returns></returns> public bool AddArticle() { try { string guid = System.Guid.NewGuid().ToString(); string Title = null; string Content = "我在测试"; DateTime dateTime = DateTime.Now; string sql = "insert into Info(Id,Title,Content,SysDate)values(@Id,@Title,@Content,@SysDate)"; SqlParameter[] param = new SqlParameter[] { new SqlParameter("@Id",guid), new SqlParameter("@Title",Title), new SqlParameter("@Content",Content), new SqlParameter("@SysDate",dateTime) }; string Conn = ConfigurationManager.ConnectionStrings["dbLink"].ConnectionString; return SqlHelper.ExecuteNonQuery(Conn, CommandType.Text, sql, param) > 0; } catch (Exception ep) { return false; } }
注释1:TransactionAutoComplete=true的时候表示没有异常的时候自动完成事物范围
第三步:显得方法AddArticle()添加不进去库,我在数据库中不准为null,看下单元测试
public void AddInfoTest() { UserInfoClient.UserInfoClient userInfo=new UserInfoClient.UserInfoClient(); int result = userInfo.AddInfo(); Assert.AreEqual(0, result); }
效果:
第四步:说明事物我们实现了,但是很多时候我们都是和别的部门或者调用别人的wcf所以需要如果其中任何一方数据出现错误就要需要回滚现在我们开始写第二个wcf接口
接口同上,现在看下方法
public class User : IUser { //使用隐式事务,并把TransactionFlowOption设置为Allowed打开事务流 [OperationBehavior(TransactionAutoComplete = true, TransactionScopeRequired = true)] [TransactionFlow(TransactionFlowOption.Allowed)] public int AddInfo() { Client.UserInfoClient userInfoClient = new UserInfoClient(); using (TransactionScope transcope = new TransactionScope(TransactionScopeOption.RequiresNew)) { try { if (!AddUser()) { Transaction.Current.Rollback(); return 0; } if (!AddArticle()) { Transaction.Current.Rollback(); return 0; } if (userInfoClient.AddInfo() != 1) { Transaction.Current.Rollback(); return 0; } else { transcope.Complete(); return 1; } } catch (Exception) { Transaction.Current.Rollback(); userInfoClient.Close(); return 0; } }
其中调用的AddUser()和AddArticle()同上面一样(这里仅仅为了测试)userInfoClient.AddInfo()这是上一个wcf部署以后的方法
现在我们先看都成功都添加成功(此时成功添加数据库)
[Test] public void AddInfoTwoTest() { Client.UserClient userClient =new UserClient(); Assert.AreEqual(1, userClient.AddInfo()); }
效果:
再看第一个wcf添加失败第二个回滚的效果
/// <summary> /// 测试回滚 /// </summary> [Test] public void AddInfoFailTest() { Client.UserClient userClient = new UserClient(); Assert.AreEqual(0, userClient.AddInfo()); }
总结:在我们一条数据插入多个表中,或者数据之间有很强的联系,我们可以考虑用事物老保证数据一致性,但是一定注意记得事物要提交,否则可能会出席死锁。大家可以动手试试
源码下载
新闻热点
疑难解答