加入收藏 | 设为首页 | 会员中心 | 我要投稿 徐州站长网 (https://www.0516zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 运营中心 > 网站设计 > 教程 > 正文

.Net中的AOP系列之构建一个汽车租赁应用

发布时间:2016-10-29 02:44:13 所属栏目:教程 来源:站长网
导读:副标题#e# 返回《.Net中的AOP》系列学习总目录 本篇目录 开始一个新项目 没有AOP的生活 变更的代价 使用AOP重构 本系列的源码本人已托管于Coding上:点击查看。 本系列的实验环境:VS 2013 Update 5(建议最好使用集成了Nuget的VS版本,VS Express版也够用

如果我们使用了不止一个数据层操作,为了使这些操作具有原子性,那么事务是必须的。也就是说,我们想要所有的数据层调用都成功(提交),要么都失败(回滚)。假设,我们可以将事务放到业务逻辑层。
假设底层的数据层会使用和.NET内置的事务类TransactionScope兼容的技术,结合try/catch块,我们可以给Accrue方法添加事务代码:

 public void Accrue(RentalAgreement agreement)
 {
     //防御性编程
     if (agreement==null)
     {
         throw new Exception("agreement为null!");
     }
     //日志
     Console.WriteLine("Accrue:{0}",DateTime.Now);
     Console.WriteLine("Customer:{0}",agreement.Customer.Id);
     Console.WriteLine("Vehicle:{0}",agreement.Vehicle.Id);
     using (var ts=new TransactionScope())//开始一个新事务
     {
         try
         {
             var rentalTimeSpan = agreement.EndDate.Subtract(agreement.StartDate);
             var numberOfDays = (int)rentalTimeSpan.TotalDays;
             var pointsPerDay = 1;
             if (agreement.Vehicle.Size >= Size.Luxury)
             {
                 pointsPerDay = 2;
             }
             var points = numberOfDays * pointsPerDay;
             //调用数据服务存储客户获得的积分
             _loyaltyDataService.AddPoints(agreement.Customer.Id, points);
             ts.Complete();//调用Complete方法表明事务成功提交
         }
         catch (Exception ex)
         {
             throw;//没有调用Complete方法,事务会回滚
         }
     }
     Console.WriteLine("Accrue Complete:{0}",DateTime.Now);
 }

记住,只有调用了事务的Complete方法,事务才会提交,否则就会回滚。如果抛出了异常,这里我们只是重新抛出,相似地,也可以在Redeem方法中使用TransactionScope,这里不再贴了,请自行看源码。

上面的代码开始变长、变丑了,原始的业务逻辑代码周围包了很多和横切关注点有关的代码块:logging,防御性编程和事务代码。

但是我们还没做完,假设底层的数据持久层偶尔会出现高流量,可能就会导致某些请求失败(比如,抛出超时异常)。如果是那种情况,执行几次重试会保持程序平滑运行(尽管在高流量期间有点慢)。通过在事务中放一个循环,每次事务回滚时,我们就增加重试次数,一旦重试次数达到限制值,我们就不管了,如下:

public void Accrue(RentalAgreement agreement)
{
    //防御性编程
    if (agreement==null)
    {
        throw new Exception("agreement为null!");
    }
    //日志
    Console.WriteLine("Accrue:{0}",DateTime.Now);
    Console.WriteLine("Customer:{0}",agreement.Customer.Id);
    Console.WriteLine("Vehicle:{0}",agreement.Vehicle.Id);
    using (var ts=new TransactionScope())//开始一个新事务
    {
        var retries = 3;//重试事务3次
        var succeeded = false;
        while (!succeeded)//一直循环,直到成功
        {
            try
            {
                var rentalTimeSpan = agreement.EndDate.Subtract(agreement.StartDate);
                var numberOfDays = (int)rentalTimeSpan.TotalDays;
                var pointsPerDay = 1;
                if (agreement.Vehicle.Size >= Size.Luxury)
                {
                    pointsPerDay = 2;
                }
                var points = numberOfDays * pointsPerDay;
                //调用数据服务存储客户获得的积分
                _loyaltyDataService.AddPoints(agreement.Customer.Id, points);
                ts.Complete();//调用Complete方法表明事务成功提交
                succeeded = true;//成功后设置为true,确保最后一次循环迭代
                Console.WriteLine("Accrue Complete:{0}", DateTime.Now);//这句移入try里
            }
            catch 
            {
                if (retries>=0)
                {
                    retries--;//直到尝试完次数时才重抛异常
                }
                else
                {
                    throw;//没有调用Complete方法,事务会回滚
                }
                
            }
        }
    }
  
}

相似地,我们也要在Redeem方法中添加,这里不做了,省略。问题越来越明显了,横切关注点基本上占据了这个方法的一半代码。但是我们还没有做完,我们需要讨论一下异常处理。

处理异常

前面不是添加了try/catch了么?难道还不够?也许!比如,服务器离线了,重试次数到达限制了,异常还是会重抛出去,如果是这种情况,我们就需要在程序崩溃前处理这个异常。
因此我们需要在防御性编程后再添加一个try/catch块包裹其他所有的代码,如下:

public void Accrue(RentalAgreement agreement)
{
    //防御性编程
    if (agreement==null)
    {
        throw new Exception("agreement为null!");
    }
    //日志
    Console.WriteLine("Accrue:{0}",DateTime.Now);
    Console.WriteLine("Customer:{0}",agreement.Customer.Id);
    Console.WriteLine("Vehicle:{0}",agreement.Vehicle.Id);
    try
    {
        using (var ts = new TransactionScope())//开始一个新事务
        {
            var retries = 3;//重试事务3次
            var succeeded = false;
            while (!succeeded)//一直循环,直到成功
            {
                try
                {
                    var rentalTimeSpan = agreement.EndDate.Subtract(agreement.StartDate);
                    var numberOfDays = (int)rentalTimeSpan.TotalDays;
                    var pointsPerDay = 1;
                    if (agreement.Vehicle.Size >= Size.Luxury)
                    {
                        pointsPerDay = 2;
                    }
                    var points = numberOfDays * pointsPerDay;
                    //调用数据服务存储客户获得的积分
                    _loyaltyDataService.AddPoints(agreement.Customer.Id, points);
                    ts.Complete();//调用Complete方法表明事务成功提交
                    succeeded = true;//成功后设置为true,确保最后一次循环迭代
                    Console.WriteLine("Accrue Complete:{0}", DateTime.Now);//这句移入try里
                }
                catch
                {
                    if (retries >= 0)
                    {
                        retries--;//直到尝试完次数时才重抛异常
                    }
                    else
                    {
                        throw;//没有调用Complete方法,事务会回滚
                    }

                }
            }
        }

    }
    catch (Exception ex)
    {
        if (!ExceptionHelper.Handle(ex))//如果没有处理异常,继续重抛
        {
            throw ex;
        }
    }
  
}

(编辑:徐州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读