加入收藏 | 设为首页 | 会员中心 | 我要投稿 徐州站长网 (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版也够用

要重构事务管理代码,这次不使用OnMethodBoundaryAspect,而是使用MethodInterceptionAspect,它不是在方法的边界插入代码,而是会拦截任何该方法的调用。拦截切面会在拦截到方法调用时执行切面代码,之后再执行拦截到的方法;而边界切面会在方法执行前后运行切面代码。

 [Serializable]
 public class TransactionManagement : MethodInterceptionAspect
 {
     public override void OnInvoke(MethodInterceptionArgs args)
     {
         using (var ts = new TransactionScope())
         {
             var retries = 3;//重试3次
             var succeeded = false;
             while (!succeeded)
             {
                 try
                 {
                     args.Proceed();//继续执行拦截的方法
                     ts.Complete();//事务完成
                     succeeded = true;
                 }

                 catch (Exception ex)
                 {
                     if (retries >= 0)
                         retries--;
                     else
                         throw ex;
                 }
             }
         }
     }
 }

这个切面例子的代码和业务逻辑中的代码基本一样,除了使用args.Proceed()方法替换了业务逻辑代码。Proceed()方法意思就是继续执行拦截到的方法。通过上面的代码,我们的代码又简化了,下面记得给服务方法添加特性,并将业务代码从事务中移除:


 [LoggingAspect]
 [DefensiveProgramming]
 [TransactionManagement]
 public void Accrue(RentalAgreement agreement)
 {
     //...略
 }

 [LoggingAspect]
 [DefensiveProgramming]
 [TransactionManagement]
 public void Redeem(Invoice invoice, int numberOfDays)
 {
     //...
 }

为了说明事务切面能正常工作,可以在OnInvoke内部前后添加Console.WriteLine("{0}方法开始/结束:{1}", args.Method.Name,DateTime.Now);,打印出来看一下。

重构异常处理切面

异常处理切面需要使用OnMethodBoundaryAspect,或者可以使用OnExceptionAspect,无论使用哪一种,样子都是差不多的。

 [Serializable]
 public class MyExceptionAspect:OnExceptionAspect
 {
     public override void OnException(MethodExecutionArgs args)
     {
         if (ExceptionHelper.Handle(args.Exception))
         {
            args.FlowBehavior=FlowBehavior.Continue;
         }
     }
 }

ExceptionHelper是我自己定义的异常处理静态类,这里出现了一个新玩意FlowBehavior,它指定了当切面执行完之后,接下来怎么办!这里设置了Continue,也就是说,如果异常处理完了,程序继续执行,否则,默认的FlowBehaviorRethrowException,这样的话,切面就没效果了,异常又再次抛出来了。

移除异常处理的代码,加上异常处理切面特性,至此,所有的横切关注点就重构完了。下面完整地看一下成品:

 [LoggingAspect]
 [DefensiveProgramming]
 [TransactionManagement]
 [MyExceptionAspect]
 public void Accrue(RentalAgreement agreement)
 {
     var rentalTime = agreement.EndDate.Subtract(agreement.StartDate);
     var days = (int) Math.Floor(rentalTime.TotalDays);
     var pointsPerDay = 1;
     if (agreement.Vehicle.Size>=Size.Luxury)
     {
         pointsPerDay = 2;
     }
     var totalPoints = days*pointsPerDay;
     _loyaltyDataService.AddPoints(agreement.Customer.Id,totalPoints);
 }


 [LoggingAspect]
 [DefensiveProgramming]
 [TransactionManagement]
 [MyExceptionAspect]
 public void Redeem(Invoice invoice, int numberOfDays)
 {
     var pointsPerday = 10;
     if (invoice.Vehicle.Size>=Size.Luxury)
     {
         pointsPerday = 15;
     }
     var totalPoints = numberOfDays*pointsPerday;
     _loyaltyDataService.SubstractPoints(invoice.Customer.Id,totalPoints);
     invoice.Discount = numberOfDays*invoice.CostPerDay;
 }

可以看到,这样的代码看着很不错吧?又回到了之前最开始的代码,只有业务逻辑的单一职责状态,所有的横切关注点都放到了它们各自的类中去了。代码非常容易阅读。

再来看看使用AOP的优点:

  1. 更改方便。如果更改了方法的方法名或参数名,切面会自动处理。切面不会关心业务逻辑是否发生变化(比如每天积分的变化),业务逻辑也不会关心你是否从Console切换到了log4Net或NLog,除非你想使用TransactionScope之外的东西处理事务或者需要改变重试次数的最大值。
  2. 可以将这些切面重复给每个服务的各个方法使用,而不是不使用AOP时,每次都要复制粘贴相似的代码。
  3. 可以在整个类、命名空间或程序集使用多广播切面,而不用在每个方法上这样写。

小结

(编辑:徐州站长网)

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

热点阅读