异常处理
未知异常处理
对于未知异常,系统采用全局统一捕获的方式进行。一般情况下,代码中不会出现try...catch代码段。当发生未知异常时系统将其类型、操作人账号、以及异常内容存入数据表中。全局统一捕获写在Program类中,代码如下:
/// 统一捕获异常,非UI线程
/// </summary>
/// <param name="sender">sender</param>
/// <param name="e">异常</param>
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (e.ExceptionObject is System.Exception)
{
CommonService.CommonClient systemLog = new CommonService.CommonClient();
systemLog.SaveLogInfo(e.ExceptionObject.ToString(), LoginInfo.CurrentUser.Account, e.ExceptionObject.GetType().ToString());
systemLog.Close();
CSMS2.Infrastructure.Helpers.AlterHelper.MessageBoxShowError(e.ExceptionObject.ToString());
}
}
/// <summary>
/// 统一捕获异常,UI线程
/// </summary>
/// <param name="sender">sender</param>
/// <param name="e">异常</param>
static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
CommonService.CommonClient systemLog = new CommonService.CommonClient();
systemLog.SaveLogInfo(e.Exception.ToString(), LoginInfo.CurrentUser.Account, e.Exception.GetType().ToString());
systemLog.Close();
CSMS2.Infrastructure.Helpers.AlterHelper.MessageBoxShowError(e.Exception.ToString());
}
由此可见对于所有的除数据库连接方面的异常,系统都会将异常信息记录数据库。这样可以比较方便的进行查询,对于数据连接方面的问题,系统判断出后,会在右下角提示“您已断网”。
已知异常
对于已知异常,系统做法比较复杂。因为已知的异常都是由开发人员根据需要,做特定处理的。框架规定了一些特殊类型,必须调用什么方法。下面一一举例说明:
- WCF的异常处理
对于WCF的客户端调用,都会使用类似如下方式进行:
DataTable dt = client.FuHeDJ_ChaXun(searchDTO);
这时ServiceProxyFactory工厂类会自动进行异常的捕获和处理,这样就避免了每个开发人员都手工的写一大堆冗余的try...catch代码段。
那么ServiceProxyFactory工厂类是怎么进行异常处理的那,让我们看下代码片段:
/// 重写调用方法
/// </summary>
/// <param name="msg">通讯数据信息</param>
/// <returns></returns>
public override IMessage Invoke(IMessage msg)
{
T channel = ChannelFactoryCreator.Create<T>(this._endpointName).CreateChannel();
IMethodCallMessage methodCall = (IMethodCallMessage)msg;
IMethodReturnMessage methodReturn = null;
object[] copiedArgs = Array.CreateInstance(typeof(object), methodCall.Args.Length) as object[];
methodCall.Args.CopyTo(copiedArgs, 0);
try
{
object returnValue = methodCall.MethodBase.Invoke(channel, copiedArgs);
methodReturn = new ReturnMessage(returnValue, copiedArgs, copiedArgs.Length, methodCall.LogicalCallContext, methodCall);
(channel as ICommunicationObject).Close();
}
catch (Exception ex)
{
if (ex.InnerException is CommunicationException || ex.InnerException is TimeoutException)
{
(channel as ICommunicationObject).Abort();
}
if (ex.InnerException != null)
{
methodReturn = new ReturnMessage(ex.InnerException, methodCall);
}
else
{
methodReturn = new ReturnMessage(ex, methodCall);
}
}
return methodReturn;
}
由上面代码可以看出,当发生超时或通信异常时,代理会调用终止方法。否则会抛出对应的异常。
- 对于事务的异常处理
通常当事务发生异常时,都要回滚所有操作。由此框架通过委托方法提供了一些通用的方法供开发人员调用。
/// 回调函数,可以不写try catch代码
/// </summary>
/// <param name="account"></param>
/// <param name="func"></param>
/// <returns></returns>
public static bool InvokeOracleTransaction(string account, Func<OracleTransaction, bool> func)
{
bool result = false;
using (OracleConnection conn = new OracleConnection(Platform.Configuration.ConfigHelper.BusinessConnString))
{
conn.Open();
OracleTransaction tran = conn.BeginTransaction(IsolationLevel.ReadCommitted);
try
{
result = func(tran);
if (result)
{
tran.Commit();
}
else
{
tran.Rollback();
}
}
catch (Exception ex)
{
SystemLog.SaveLogInfo(ex.ToString(), account, ex.GetType().ToString());
tran.Rollback();
#if DEBUG
throw ex;
#else
return false;
#endif
}
}
return result;
}
由上可以发现,当事务异常时会回滚所有操作,并记录日志。开发人员只需调用该方法,并传入相应的事务和方法即可。
- 一些控件的异常
比如扫描控件、Grid控件系统都会将原异常捕获,然后加上特殊的中文描述,使得客户和开发人员能够比较方便的判断异常的成因。
{
// 设置鼠标
this.Cursor = Cursors.Default;
// 抹去框架
//Graphics g = this.CreateGraphics();
//DrawSelectionFrame(g);
//g.Dispose();
// 标准化点
NormalizePoints(ref startImgP, ref endImgP);
size.Width = endImgP.X - startImgP.X + 1;
size.Height = endImgP.Y - startImgP.Y + 1;
rec.Size = size;
Crop cp = new Crop(rec);
// 剪切图像
ApplyFilter(cp);
}
catch (Exception ex)
{
if (this.languageType == "zh-CN")
MessageBox.Show("对不起!切图出错:" + ex.Message, "系统提示");
else
MessageBox.Show("Cutting image error:" + ex.Message, "System Information");
}
- 一般的业务操作的异常
对于一般业务操作,系统会记录异常,并将message向上抛出,然后由对应的界面决定到底返回什么错误信息给客户。这里的message已经是经过加工后的消息了,具体怎么加工是由开发人员根据情况处理的。
/// 工单登记
/// </summary>
/// <param name="message"></param>
/// <param name="dto"></param>
/// <param name="login"></param>
/// <returns></returns>
public static bool DengJi(out string message, FuHeDJDTO dto, Entity.BW_YUYUEXX yuyuexx, UploadEntity[] uploadEntityList, Entity.LoginInfo login)
上面是对于工单登记的方法接口,如有错误会已out message的形式返回。