.NET 异常处理的动作策略(Action Policy)

SQL Server 2008基于策略的管理 ,基于策略的管理(Policy Based Management),使DBA们可以制定管理策略,并将这些策略应用到服务器、数据库以及数据环境中的其他对象上去。基于动作策略(Action Policy)的异常处理使开发人员可以为异常处理制定策略,简单的说,动作策略只是一些可重复使用的一个装饰器,可以很容易应用与方法调用。

异常处理只是一个合乎逻辑的动作策略的一部分,动作策略决定如何对异常做出处理,微软的Enterprise Library 的异常处理模块试图为开发 人员和policy制定者为整个企业级应用程序各层的异常处理创建一致的策略。但是,异常处理的策略是硬编码的(也就是简单的try-catch代码 块)。

如下是简单的示例代码:

try

{

customersDataSet = RunQuery(“GetAllCustomers”);

}

catch(Exception ex)

{

bool rethrow = ExceptionPolicy.HandleException(ex, “Data Access Policy”);

if (rethrow)

throw;

}

红色部分的Exception Policy是硬编码的,这里我给你介绍一种更好的基于动作策略(Action Policy)的异常处理,这个异常处理策略的的原理是充分利用C#的闭包Action<Action>,这个在园子里有很多讨论:

《你不 常用的c#之三》:Action 之怪状

利用 Reflector把"闭包"看清楚

Action<Action>其实就是一个委托

public delegate void Action();
public delegate void ActionPolicy(Action action);

很容易我们就可以实现一个出现异常情况下重试3次的策略

void MyRetryPolicy(Action action)
{
int counter = 0;
while (true)
{
try
{
      action();
return;
}
catch (DbException ex)
{
      counter+=1;
if (counter==3)
{
throw;
}
Thread.Sleep(1);
}
}
}

这个策略可以这样用

// 没有参数和返回值的简单调用
MyRetryPolicy(() => LongRunningDbCall());
MyRetryPolicy(() => AnotherFragileCall());
// 有一个参数和返回值的调用
int result = 0;
MyRetryPolicy(() => result = CreateRecords(records));
上述代码包含了两部分的逻辑,异常处理和行动策略。这个代码不 够通用,可以包装一个行动策略。下面介绍一下在我的项目中使用的行动策略,我使用Autofac模块包装了行动策略,代码如下:

public class ActionPolicyModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.Register(c => ComposeActionPolicy(c));
            base.Load(builder);
        }

        static ActionPolicy ComposeActionPolicy(IComponentContext context)
        {
            ILog log = context.Resolve<ILog>();
            var actionPolicy = ActionPolicy.With(e => CompositeExceptionHandler(e, log))
               .Retry(3, (ex,i) =>
               {   
                   log.DebugFormat(ex, "Retrying exception for the {0} time", i);
                   SystemUtil.Sleep((0.5 + i).Seconds()); 
               });

;
            return actionPolicy;
        }

        static bool CompositeExceptionHandler(Exception ex, ILog log)
        {
            ExceptionCounters.Default.Add(ex);

            if (ex.Message.Contains("Password"))
            {
                throw new SecuredException(ex);
            }

            if (ex is DbException)
            {
                return true;
            }

            log.Error(ex, "Unrecoverable exception");
            return false;
        }
    }

我们的方法中对策略的调用的代码很简单

        public void AddAppliction(Application appInfo)
        {
            _scopes.Validate(appInfo, "AppInfo", AppRules.Appliction);
            _log.DebugFormat("AddAppliction()");
            _policy.Do( () => appRepository.AddAppliction(appInfo));
        }

_policy 就是一个ActionPolicy对象,来自于Autofac内的策略配置,这样我们就可以在我们的代码中去除类似于微软的Enterprise Library 的异常处理模块的硬编码代 码。细心的你注意到了红色的代码中使用一个Retry Action Policy,出错的时候重试三次,每次之间间隔时间依次加长,重试了三次都不成功才抛出异常,这是一个很有用的功能,比如在数据库发生死锁的时候。动作 策略还支持一种断路器,我们的生活中有一种很常见的电路断路器 ,在发生电涌或过载的时候保护我们的电路,我们的分布式系统中也会碰到类似的现象。

 

 

 

今天在博客园看到一个帖子:
地址:http://www.cnblogs.com/foundation/archive/2008/12/11/1352797.html
我转载如下
例1:

static   void  Main( string [] args)
{
List
< Action >  ls  =   new  List < Action > ();
for  ( int  i  =   0 ; i  <   10 ; i ++ )
{ ls.Add(() 
=>  Console.WriteLine(i));
}
foreach  (Action action  in  ls)
{
action();
}
System.Console.Read();
}

 

输出结果:

例2:


  
  
static   void  Main( string [] args)



{



List
< Action >  ls  =   new  List < Action > ();



for  ( int  i  =   0 ; i  <   10 ; i ++ )



{



int  tp = i;



ls.Add(() 
=>  Console.WriteLine(tp));



}



foreach  (Action action  in  ls)



{



action();



}



System.Console.Read();



}






则结果为:

为啥呢?我们来用windbg分析下:

对于例1,我们观看堆上的Action对象(System.Action )只有一个,
heap上的列表:

6c27166c 1 32 System.Action

你的foreach里其实都是循环的一个action
所以最后的时候是10(i的最后值)

而例2中则有10个
action对象在heap上的列表:

6c27166c 10 320 System.Action


10个,每个action里引用一个tp,
heap列表:

Address MT Size
01451790 6c27166c 32
014517dc 6c27166c 32
01451808 6c27166c 32
01451834 6c27166c 32
01451860 6c27166c 32
014518bc 6c27166c 32
014518e8 6c27166c 32
01451914  6c27166c 32
01451940 6c27166c 32
014519bc 6c27166c 32

随便看一个Address,比如这个01451914 ,dump出来的值是

MT Field Offset Type VT Attr Value Name
79102290 4000001 4 System.Int32 1 instance 7 tp

看到没,此时的tp还存在,值为7。

为了验证,我们可以

Action a1  =  ls[ 0 as  Action;
Action a2 
=  ls[ 1 as  Action;
Console.WriteLine(
object .ReferenceEquals(a1,a2));

第一个例子true,第二个(tp) 则为false。

看来我用windbg有点越俎代庖。

更确切说是VS编译器优化的问题,从MSIL代码可以看出。

可以参考杨过兄的分析:

http://www.cnblogs.com/yjmyzz/archive/2009/03/13/1410924.html (利 用Reflector把"闭包"看清楚 )


希望本文能对你有所帮助,谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值