171

.Net高级进阶,在复杂的业务逻辑下,如何以最简练的代码,最直观的编写事务代码? -...

 6 years ago
source link: http://www.cnblogs.com/1996V/p/7798111.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

.Net高级进阶,在复杂的业务逻辑下,如何以最简练的代码,最直观的编写事务代码?

本文将通过场景例子演示,来通俗易懂的讲解在复杂的业务逻辑下,如何以最简练的代码,最直观的编写事务代码。

用一句话贯穿全文就是:通过委托来让TransactionScope的编码实现更直观,并不是讲述TransactionScope的其它如分布式、ACID等场景应用及解决方案。

通过一系列优化最终达到两个效果,1.让不了解TransactionScope的童鞋通过代码块TransactionScope来控制事务,2.让了解TransactionScope的童鞋简单概述本实例的委托思想抛砖引玉来优化TransactionScope的编码写法。

本文需要的知识点:1. Action委托  2. 分布式事务TransactionScope(不懂不要紧,文中会通过示例一和示例二简单讲解这是啥,为啥要用)

----------------------

 .Net Web开发技术栈

.Net高级进阶,教你如何构建企业模型数据拦截层,动态控制字段验证

.Net高级进阶,在复杂的业务逻辑下,如何以最简练的代码,最直观的编写事务代码?

web安全:通俗易懂,以实例讲述破解网站的原理及如何进行防护!如何让网站变得更安全。

web安全:QQ号快速登录漏洞及被盗原理

.Net,Dll扫盲篇,如何在VS中调试已经编译好的dll?

----------------------

示例一和示例二,主要是来讲解 TransactionScope 是什么,为什么要用TransactionScope。
示例三(重要)则是优化写法,增加代码的灵活性和可读性。

【示例一】

现在,你要写个入库接口,大致意思就是: 勾选一条商品,然后写上数量,点击入库按钮,将会产生一条入库记录,同时  这个商品的所对应的  库存数量 也会 更新。
因为涉及到库存,所以要用事务来保证数据安全。

StorageTask:入库作业表,存写入库记录

GoodsInventory:商品库存表, 里面放的是  不同商品的 详细介绍、数量等信息

那么我们的实现  ,  可能是 这样的 , 如图:

1026815-20171107163110903-1920650891.png

上图的代码,我们主要是先看  商品入库操作  GoodsInventoryOperate  这个Dal方法,放图:

 

1026815-20171107164306966-2134065399.png

上面的这是一个Dal方法,事务写法很大众,很常规,代码没毛病。

【示例二】

好,现在,我们的业务要求要改一下,改成这样的:

 勾选了一条商品,输入该条商品的入库数量,然后又勾选了一条原材料,输入该原材料的入库数量,最终点击入库按钮,要  产生 商品的入库记录和原材料的入库记录, 还要 分别修改 所对应的 商品库存表和原材料库存表 的 库存数量

那么,我们就要修改下这个接口,首先,参数由原来的   单行的参数  改为  集合形式的 参数,

1026815-20171107175958309-718625223.png

那么我们的接口代码也随之修改,如下图:

1026815-20171107180115325-954519432.png

然后我们在看看 这个入库操作方法 InventoryOperate

1026815-20171107180228434-464681763.png

我们来对比下,我们把之前的 商品入库操作  GoodsInventoryOperate 方法 给改成了  入库操作方法 InventoryOperate 。

实际上,入库操作方法 InventoryOperate =  商品入库操作  +  原材料入库操作 ,但是因为 业务的更改,让我们不得不把 原本 Dal层中的两个方法代码 给 复制粘贴到一起,形成第三个方法,也就是入库操作方法 InventoryOperate 。

那么,有没有一种写法,能让我们 更简单更方便  不用每次复制粘贴代码形式 来实现 事务的编写?

TransactionScope:

  在早期.net时代,如果想使用事务,就用SqlTransaction来实现,而每个SqlTransaction都会用同一个SqlConnection连接对象。
  如果逻辑简单还好说,如果逻辑稍微复杂的话,想用多个Dal方法来共同组合一个事务的话,就非常费脑筋的,就像上文这样演变的 第一版 和 第二版。
  为此,在.Net2.0时代,TransactionScope诞生了,微软官方描述:代码块事务,还有一个别称:分布式事务。
  它实现了IDisposable接口,可以把它被实例化开始到被Dispose掉之间的代码作为一个事务,也就是它的存在,最终让你的代码块所嵌套在其中多个DAL方法变成“一个方法”

那么,当我们使用它以后,我们就可以这样编写:

1026815-20171107184205497-235863506.png

【示例三】

 现在,大家对 TransactionScope 有了基本的印象,那么现在考虑到代码的可读性和灵活性,我将要对当前风格再次改写,通过委托的形式让代码结构层次更加分明。

 1         /// <summary>
 2         /// 事务语句统一执行
 3         /// </summary>
 4         /// <param name="ac">委托</param>
 5         /// <returns></returns>
 6         public static bool TransactionExecute(Action ac)
 7         {
 8             try
 9             {
10                 using (TransactionScope ts = new TransactionScope())
11                 {
12                     ac.Invoke();
13                     ts.Complete();
14                 }
15                 return true;
16             }
17             catch
18             {
19                 return false;
20             }
21         }    

然后,我们的接口方法的编码变成了这样:

 1         /// <summary>
 2         /// 商品仓库的入库作业操作
 3         /// </summary>
 4         /// <param name="iData">入库数据集合</param>
 5         /// <returns></returns>
 6         public string WarehouseGoodsOperate(List<InboundModel> iData)
 7         {
 8             Action ac = () => { };//声明一个委托
 9             foreach (InboundModel item in iData)
10             {
11                 if (item.type == "商品")
12                 {
13                     ac += () =>
14                     {
15                         IServices.Insert(item);
16                         IServices.UpdateGoods(item);
17                     };
18                 }
19                 if (item.type == "原材料")
20                 {
21                     ac += () =>
22                     {
23                         IServices.Insert(item);
24                         IServices.UpdateInventory(item);
25                     };
26                 }
27             }
28             if (IServices.TransactionExecute(ac))
29             {
30                 return "成功";
31             }
32             return "失败";
33         } 

 通过上面这样的写法,最终让代码风格更干净,同时在 事务的 处理上更灵活方便, 我们只需要把想要执行的 方法 让 ac 给包进去, 最后在调用 TransactionExecute 统一执行。

 基于自己的场景可以定制自己的TransactionExecute,本文着重指出利用委托来优化该情况下的编码思想,至于TransactionExecute,这里只是做个简单的科普,其中有更多可挖掘的地方,感兴趣的童鞋可以自行百度。

 当然,采用这种委托写法,需要注意一点:

1026815-20171107193058575-1893132159.png

因为传递的是引用,并且用用到了lambda,导致了闭包,最终在Invoke时在匿名类中会用同一引用。

那么,怎样解决这样情况?
逐个逐个的赋值,或者用反射?
不用,我们可以通过继承 ICloneable 接口,然后通过浅复制的方式实现Clone方法(浅复制拷贝时,string会创建新的实例,如果尚有除string之外的引用类型还需深拷贝)。

    class SysUser : ICloneable
    {
        public object Clone()
        {
            return this.MemberwiseClone(); 
        }
    }

最后,我们就可以这样:

1026815-20171107193648122-1869336754.png

让正确的程序更快比让快速的程序正确要容易的多

我喜欢和我一样的人交朋友,不被环境影响,自己是自己的老师,欢迎加群 .Net web交流群, QQ群:166843154 欲望与挣扎

作者:小曾
出处:http://www.cnblogs.com/1996V/p/7798111.html  欢迎转载,但任何转载必须保留完整文章,在显要地方显示署名以及原文链接。如您有任何疑问或者授权方面的协商,请给我留言
.Net交流群, QQ群:166843154 欲望与挣扎 

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK