标题:没头没尾--项目开发笔记:异常处理与日志记录代码自动生成(工具能生成多少代码!?续一)
关键词:分布式开发 C# 项目分工 DELPHI与C#的混合开发 AOP 代码生成工具 异常处理 日志记录
12月7号:从AOP的想法中得到对项目中异常与日志记录的抽离与处理方式
上次写的代码生成工具是从系统的方面来讲代码生成工具。写的比较粗,只是定义出了代码生成工具在我们的项目中对应的目的以及实现几种手段。可能比较抽象。下面用本项目中对异常处理与日志记录处理的处理方式来解释代码生成工具的应用。
l 异常处理与日志记录的统一处理机制
这里说的异常处理与日志记录的统一处理是指部分处理。整个系统中,对于异常处理与日志的处理只可以在任何的地方来做。这种方案只是将80%的日志与异常处理使用代码生成工具来进行生成。对于特殊的异常处理,还是每个程序员自己去搞定J。
异常处理与日志的记录处理应该是完全不同的两件事件。但是基本上我认为这两件事件都是独立于业务过程,并且在我们的系统中,异常的事件必须要记录到日志处理。所以我考虑统一进行处理。
项目开始的时候,我去读了几本有关C#的参考资料。根据我以前开发JAVA应用服务器的经验,C#对应的类可以将例外顺着调用的栈一直将例外抛到最外层。跟踪C#代码的时候我也发现C#中往往做一个非常简单的调用时,也可能要调用很多层的代码。(microsoft的习惯,以前写MFC的时候就发现了L。)所以异常处理的外抛机制我想应该是必不可少的。
老实说,项目开始的时候我并没有考虑清楚日志与异常处理应该如何去做。只是有一个不是非常明确的想法是要将这两个处理的过程一起进行考虑。于是现在,在项目已经进行到一半的时候开始对这个问题进行统一的处理。不得不用代码生成工具,要不会麻烦死的。
第11期的程序员杂志中介绍了有关AOP的方法,我觉得获益匪浅。我想AOP的核心无疑是面向方面这四个字。至于具体的实现,目前给出的并没有针对C#的版本。那么我们只能去应用它的思路。我觉得他的思路中比较明确的把开发程序中的关注对象分开,从而对开发进行的划分。但是我并没有看出对开发过程人员分工进行重新的组织。
(每我想了想如果我们写的是一个没有异常处理机制的库,从外部使用AOP的事情消息截取方式可能没有办法得出去截取错误的消息以及记录。只通过截取消息的方式可能是无法达到统一处理异常的机制?不知道朋友们对这个问题有什么看法。)
我们的系统中,层次的划分之后,服务器端调用可以分成两个大的部分。Web Service是根据客户端的请求各自的定义。而其它的,如Facade、Business Rules,DO等等都是以库的形式存在。(可以在Web Service的BIN的目录找到一堆的DLL)。针对这种情况,项目组定义的原则是库中最好不要捕获异常(除非某个异常表示的是代码可以处理的情况),而且从Web Service调用代码可以处理它们。(参考C#高级编程一书。)
最后项目小组商量的结果就是利用C#可以将例外可以一直抛出到最外层的异常处理原理。只在最外层的Web Service层写统一异常例外的处理。(特殊的异常处理每个人再自己写。)并且由于Facade层与Web Service层所定义的结构都是以面向用户设计来进行处理的。(可以参考上一篇:工具能生成多少代码!?)也就是说结构是完全相同的。例如:
如果Facade层的ProductFacade.cs有一个以下的函数:
/// <summary>
/// Update all change Product Data item.
/// </summary>
public int ApplyAllChangeItem(String XMLStringofProductData)
{
……………………
}
那么Web Service层中一定会对应有ProductService.asmx.cs。其中会有以下方法:
[WebMethod(EnableSession=true,Description = " Update all change Product Data item.")]
public int ApplyAllChangeItem(String XMLStringofProductData)
{
return new ProductFacade().ApplyAllChangeItem(XMLStringofProductData);
}
(代码段一)
上面的Web Service的方法是不包括对异常与日志处理,如果加上对日志与异常的处理,这个方法是这样写的。
[WebMethod(EnableSession=true,Description = " Update all change Product Data item.")]
public int ApplyAllChangeItem(String XMLStringofProductData)
{
try
{
return new ProductFacade().ApplyAllChangeItem(XMLStringofProductData);
}
catch(Exception e)
{
try
{
lasterror = e.Message;
throw new ExceptionEx(e);
}
catch
{
HttpContext.Current.Response.Redirect("Common.asmx/GetErrMessage?", false);
return null;
}
finally
{
}
}
catch
{
try
{
lasterror = "UnknowError!@@";
throw new ExceptionEx(lasterror);
}
catch
{
HttpContext.Current.Response.Redirect("Common.asmx/GetErrMessage?", false);
return null;
}
finally
{
}
return null;
}
finally
{
}
}
(代码段二)
(注:
u ExceptionEx是我们自己定义的例外类,开发人员可以在开发过程出现程序出现异常时自己去抛出这个例外。也可以不去自已写例外的处理。出了错dotnet自己会抛出的。
u ExceptionEx的负责将例外统一进行日志处理。只要new ExceptionEx就会记录日志。
)
l 代码生成工具的应用
上面的对异常问题的分析实际上在一开始我并没有想清楚。也就是说,系统开始时,我们并没有考虑怎么样去统一异常的处理。(不知道还没有朋友们也是这样做的??)于是先开始只与负责数据库部分的同仁一起做出数据库连接时例外处理的例子。统一了那个过程。(已经有朋友指出我们是鲁莽编程,郁闷。还是我的经验不够。)而且在上面这个异常与日志方案没有出来之前,我已经生成了部分的Web Service的代码。所以当这个处理方式定义下来之后,我们要做的是两件事情:
1. 修改已经完成的代码,加入统一的处理
2. 根据Facade层自动生成Web Service的代码
下面分别针对两种情况来说明代码生成工具;
² 修改已经完成的代码,加入统一的处理
还是用上一篇的说法,制作一个代码生成工具一定要搞清楚三个问题,输入、生成方法以及输出分别是什么?
针对已经完成的代码,一定是修改已经在Web Service目录中已经有的Web Service文件。修改的部分一定是要针对每个Web Service类中的Web Service的方法进行处理。然后将try之类的异常处理的部分加入进去。下面是具体过程的描述:
1. 通过输入得到Web Service的目录;
2. 遍历目录,找出所有最后八个字符是“.asmx.cs”的文件;(Web Service的类文件)
3. 将文件内容读入一个文件字符串中;
4. 循环查找以 “[WebMethod(”为开头的字符串位置;
5. 找出上面步骤字符串位置之下第一个“{”的位置,然后再根据花括号的对应关系找出与第一个“{”对应的“}“的位置;
6. 通过4与5将Web Service的函数提取出来,存入一个函数字符串中。
7. 在第一个“{”的位置之后插入“try{”;(try的字符是从模板文件中读入的)
8. 在最后一个“{”的位置之前插入“catch………………”;(catch的字符是从模板文件中读入的)
9. 将新生成函数字符串替换文件字符串中原有的函数字符串
10. 反复3-9步骤,直至文件字符串尾;
11. 将文件字符串写入文件中,替代原来的文件。
通过这个过程,就可以将上面所有类似于代码段一的代码都生成代码段二的代码。开发人员可以不去重复的写try与catch的代码。
如果在开发过程中加入了一种新的异常类(比如生成ExceptionEx1, ExceptionEx2),我们所要做的就是修改模板文件,更新Web Service的所有文件就好。
² 根据Facade层自动生成Web Service的代码
这个过程与修改过程不一样的地方就是输入并不是读取已经存在的Web Service文件,而是读取Facade层的代码,生成Web Service文件。下面是具体的过程描述:
1. 通过输入得到Facade层的目录以及Web Service的目录;
2. 读取Web Service生成模板。将其读入一个大的字符串中。
3. 遍历Facade目录,找出所有最后九个字符是“facade.cs”的文件;(我们定义的facade类文件);设为XXXfacade.cs
4. 将文件内容读入一个文件字符串中;
5. 循环查找以 “/// </summary>”为开头的字符串位置;(生成Facade方法时加入注释的最后一条。)
6. 在Web Service字符串中加入一个新的方法的字符串;
7. 找出上面步骤字符串位置之下第一个“{”的位置,然后再根据花括号的对应关系找出与第一个“{”对应的“}“的位置;
8. 通过4与5将Web Service的函数提取出来,存入一个函数字符串中。
9. 读出函数字符串中Facade的方法。(如果是函数属性是private则跳过。)读出函数字符串中Facade方法的参数。
10. 修改Web Service字符串中的替换字符,将Facade的方法以及参法分别写入。
11. 反复3-10步骤,直至Facade文件字符串尾;
12. 将新生成的Web Service字符串写入XXXService.asmx.cs以及XXXService.asmx文件中。(XXXService.asmx文件的内容很少,与函数无关。)
通过这种方式生成的web service的代码将会对应所有的facade层的类与函数。并且将异常例外处理进行了统一的处理。减少了开发人员的工作量以及出错的机会。使调试可以找到出错的日志,有利于调试过程。
目前还没有看出这样生成的代码有什么大的问题,希望这么做过的朋友们给给我好的建议。
还有,我对代码生成工具产生的代码的效率问题有一些想法,回头与朋友们交流吧。
如果对项目的前因后果不清楚,可以参考以前的文档: