自定义异常框架:使用 Enterprise Library 异常处理块






3.95/5 (16投票s)
本文演示了一个使用 Enterprise Library 异常处理块创建的自定义异常处理模型的实际工作示例,该模型围绕 MVP 架构构建。
引言
本文演示了如何使用 Microsoft Enterprise Library 异常处理块创建自定义异常层。本文创建的演示项目采用了模型-视图-呈现器 (MVP) 架构,并在其中内置了自定义异常层。
系统要求
- 安装 Microsoft Enterprise Library .NET 2.0
- .NET Framework 2.0
重要命名空间
Microsoft.Practices.EnterpriseLibrary.ExceptionHandling
- Microsoft.Practices.EnterpriseLibrary.Common.dll
- Microsoft.Practices.ObjectBuilder.dll
- Microsoft.Practices.EnterpriseLibrary.Common.dll
- System.Collections.Specialized.dll
- System.Configuration.Assemblies.dll
自定义异常块的功能
- 自定义块根据层来划分异常。例如:数据访问层异常、呈现器层异常、服务层异常等。
- 此自定义异常还针对系统层异常,如关键异常。例如:数据库服务器宕机、Web 服务不可用等。
- 此自定义异常还处理已处理的异常,并向用户显示自定义消息。例如:在数据库中插入重复记录、从数据库抛出异常,并在 .NET 应用程序中进行处理,然后向上冒泡以显示用户定义的警告消息。因此,这最终区分了警告、错误和异常。对于未处理的系统错误,系统将重定向到一个错误页面。对于警告或操作信息,它将显示在同一个功能屏幕上,以避免中断正常执行。
- 此块已集成到模型-视图-呈现器 (MVP) 架构中。
配置设置
配置设置封装了应用程序所需的各种策略。这些策略包括:
- 全局策略:识别来自各个层的实际异常。
- 传播策略:此策略将层抛出的实际异常传播到主流程。
- 自定义策略:呈现器/数据访问/视图/服务。
策略 | 后处理操作 | Layer | |
1 | 全局 | 无 | Project.Practice.MVP.ExceptionHandling |
2 | 传播 | NotifyReThrow (通知并重新抛出) |
System.Exception |
3 | 自定义:呈现器层 | ThrowNewException (抛出新异常) |
Project.Practice.MVP.Presenter |
4 | 自定义:数据访问层 | ThrowNewException (抛出新异常) |
Project.Practice.MVP.DataAccess |
5 | 自定义:视图层 | ThrowNewException (抛出新异常) |
Project.Practice.MVP.Web |
Project.Practice.MVP.ExceptionHandling: 自定义异常类
带有 MVP 模型的异常处理类的实际表示形式如下所示。
下面的类图描绘了异常处理类结构的图。创建了所有自定义异常类的层。这些类本身继承自 `BaseException` 类。
Project.Practice.MVP.ExceptionHandling: ApplicationExceptionHandler.cs
在此类中,我们有一个名为 `HandleException` 的方法,该方法实现了 `IExceptionHandler` 接口方法。
namespace Project.Practice.MVP.ExceptionHandling
{
[ConfigurationElementType(typeof(CustomHandlerData))]
public class ApplicationExceptionHandler : IExceptionHandler
{
private const string UNEXPECTED_ERROR = "Unexpected Error!!";
public ApplicationExceptionHandler(NameValueCollection ignore)
{
}
#region IExceptionHandler Members
public Exception HandleException(Exception exception,
Guid correlationID)
{
try
{
/* This is Critical Layer Exception.
For E.g Database Unavailable
Web Service Unavailable and so on .*/
if (exception.GetType().Equals(
typeof(CustomException.CriticalException)))
{
if (HttpContext.Current.Items.Contains("ERROR_MSG"))
{
HttpContext.Current.Items.Remove("ERROR_MSG");
}
HttpContext.Current.Items.Add("ERROR_MSG", exception.Message);
HttpContext.Current.Server.Transfer(
"~/CriticalException.aspx", false);
}
//This is Presenter Layer Exception.
else if (exception.GetType().Equals(
typeof(CustomException.PresenterLayerException)))
{
if ((CustomPage)HttpContext.Current.Handler != null)
{
((CustomPage)HttpContext.Current.Handler).Error =
"Presenter Layer Exception :" +
exception.InnerException.Message ;
}
}
//This is View Layer Exception.
else if (exception.GetType().Equals(
typeof(CustomException.ViewLayerException)))
{
if ((CustomPage)HttpContext.Current.Handler != null)
{
((CustomPage)HttpContext.Current.Handler).Error =
"View Layer Exception:" + exception.Message;
}
}
//This is DataAccess Layer Exception
else if (exception.GetType().Equals(
typeof(CustomException.DataAccesLayerException)))
{
if ((CustomPage)HttpContext.Current.Handler != null)
{
((CustomPage)HttpContext.Current.Handler).Error =
"DataAccess Layer Exception: "+ exception.Message;
}
}
//This is SQL DAL Layer Exception
else if (exception.GetType().Equals(
typeof(CustomException.SQLDALException)))
{
if ((CustomPage)HttpContext.Current.Handler != null)
{
((CustomPage)HttpContext.Current.Handler).Error =
"SQL Error 101: " + exception.Message;
}
}
else
{
if ((CustomPage)HttpContext.Current.Handler != null)
{
//This is Session Expired Exception
if ((((CustomPage)
HttpContext.Current.Handler).CurrentSession == null))
{
HttpContext.Current.Items["ERROR_MSG"] = "Access Denied";
HttpContext.Current.Server.Transfer(
"~/CriticalException.aspx", false);
}
//This is General Exception:UN_DEFINED EXCEPTION
HttpContext.Current.Server.Transfer(
"~/CriticalException.aspx", false);
}
}
}
catch (System.Threading.ThreadAbortException ex)
{
}
catch (Exception ex)
{
}
return exception;
}
#endregion
}
}
Project.Practice.MVP.ExceptionHandling: ExceptionHandleProvider.cs
这是从 Web 页面调用该方法的,当异常冒泡到此处时。
namespace Project.Practice.MVP.ExceptionHandling
{
public class ExceptionHandleProvider
{
public static bool HandleException(Exception exception, string PolicyName)
{
bool reThrow = false;
reThrow = ExceptionPolicy.HandleException(exception, PolicyName);
return reThrow;
}
}
}
Project.Practice.MVP.DataAccess:DataProvider
这是一个部分类。我们有一个名为 '`DataProvider`' 的部分类,每个模块都有一个不同的文件名。在此方法中,如果异常是数据访问层中的关键异常,它将跨层传播以显示为关键异常;但如果存在数据访问异常,它将通过其他层传播以显示为数据访问层异常。
namespace Project.Practice.MVP.DataAccess
{
public partial class DataProvider
{
public static void ProcessDataAccessLayerException(Exception ex)
{
bool reThrow = false;
if (ex.GetType().Equals(typeof(CriticalException)) == true)
{
reThrow = ExceptionPolicy.HandleException(ex, "Propogate Policy");
if (reThrow)
{
throw ex;
}
}
else if (ex.GetType().Equals(typeof(SQLDALException)) == true)
{
reThrow = ExceptionPolicy.HandleException(ex, "Propogate Policy");
if (reThrow)
{
throw ex;
}
}
else
{
reThrow = ExceptionPolicy.HandleException(ex, "DataAccess Layer Policy");
if (reThrow)
{
throw ex;
}
}
}
}
}
现在我们有一个部分类 `DataProvider`,以及一个名为 *MathOperationDataProvider.cs* 的文件模块。
数据访问:抛出异常
namespace Project.Practice.MVP.DataAccess
{
public partial class DataProvider
{
/// <summary>
/// Get DemandList
/// </summary>
/// <returns></returns>
public static int AddOperation(int result)
{
string error = "SQL_DAL_EXCEPTION";
try
{
if (error == "SQL_DAL_EXCEPTION")
{
throw new SQLDALException("SQL Store Procedure Error") ;
}
if (error == "DATA_ACCESS_LAYER")
{
throw new DataAccesLayerException("Data Not Found Error");
}
if (error == "CRITICAL EXCEPTION")
{
throw new CriticalException();
}
}
catch(Exception ex)
{
ProcessDataAccessLayerException(ex);
}
return result;
}
}
}
Project.Practice.MVP.Presenter
BasePresenter.cs
namespace Project.Practice.MVP.Presenter
{
public class BasePresenter
{
public IServicesProvider m_Service = null;
public BasePresenter()
{
m_Service = new Services.ServicesProvider();
}
public void ProcessPresenterLayerException(Exception ex)
{
bool reThrow = false;
if (ex.GetType().Equals(typeof(CriticalException)))
{
reThrow = ExceptionPolicy.HandleException(
ex, "Propogate Policy");
if (reThrow)
{
throw ex;
}
}
else if (ex.GetType().Equals(typeof(ViewLayerException)))
{
reThrow = ExceptionPolicy.HandleException(
ex, "Propogate Policy");
if (reThrow)
{
throw ex;
}
}
else if (ex.GetType().Equals(typeof(DataAccesLayerException)) == true)
{
reThrow = ExceptionPolicy.HandleException(
ex, "Propogate Policy");
if (reThrow)
{
throw ex;
}
}
else if (ex.GetType().Equals(typeof(SQLDALException)) == true)
{
reThrow = ExceptionPolicy.HandleException(
ex, "Propogate Policy");
if (reThrow)
{
throw ex;
}
}
else
{
reThrow = ExceptionPolicy.HandleException(
ex, "Presenter Layer Policy");
if (reThrow)
{
throw ex;
}
}
}
}
}
CalculationPresenter.cs
namespace Project.Practice.MVP.Presenter
{
public class CalculationPresenter : BasePresenter
{
#region Public Members
private ICalculationView m_View = null;
#endregion
#region Public Constructor
public CalculationPresenter(ICalculationView view)
: base()
{
m_View = view;
}
#endregion
public void Initialize()
{
//Security Check
//If(user session expires Or login user fails
//Throw New CriticalLayerException()
}
public void AddNumbers()
{
try
{
m_View.Result= Convert.ToString(AddOperations());
}
catch (Exception ex)
{
ProcessPresenterLayerException(ex);
}
}
private int AddOperations()
{
int result = m_View.Numbers1 + m_View.Numbers2;
return m_Service.AddOperation(result);
}
}
}
Project.Practice.MVP.Web
public partial class Calculation : CustomPage,ICalculationView
{
#region Private Members
private CalculationPresenter m_Presenter = null;
#endregion
protected void Page_Load(object sender, EventArgs e)
{
try
{
m_Presenter = new CalculationPresenter((ICalculationView)this);
if (ViewState["USER_KEY_VALUE"] == null)
{
throw new ViewLayerException("Code Behind Exception");
}
}
catch (Exception ex)
{
if (ExceptionHandleProvider.HandleException(
ex, "Global Policy") == true)
{
throw ex;
}
}
}
#region ICalculationView Members
public int Numbers1
{
get
{
return Convert.ToInt32(txtNumber1.Text);
}
}
public int Numbers2
{
get
{
return Convert.ToInt32(txtNumber2.Text);
}
}
public string Result
{
set
{
lblResult.Text = value;
}
}
#endregion
protected void Button1_Click(object sender, EventArgs e)
{
try
{
m_Presenter.AddNumbers();
}
catch (Exception ex)
{
if (ExceptionHandleProvider.HandleException(
ex, "Global Policy") == true)
{
throw ex;
}
}
}
public override string Error
{
set {pnlError.Visible = true;
lblException.Text = value; ; }
}
}
最终展示
此异常从 ASPX 页面的代码隐藏中抛出或引发。
protected void Page_Load(object sender, EventArgs e)
{
try
{
m_Presenter = new CalculationPresenter((ICalculationView)this);
if (ViewState["USER_KEY_VALUE"] == null)
{
throw new ViewLayerException("Code Behind Exception");
}
}
catch (Exception ex)
{
if (ExceptionHandleProvider.HandleException(
ex, "Global Policy") == true)
{
throw ex;
}
}
}
此异常从数据访问层引发。只需更改错误变量即可测试此层。
string error = "DATA_ACCESS_LAYER";
try
{
if (error == "SQL_DAL_EXCEPTION")
{
throw new SQLDALException("SQL Store Procedure Not Found") ;
}
if (error == "DATA_ACCESS_LAYER")
{
throw new DataAccesLayerException("Data Not Found Error");
}
if (error == "CRITICAL EXCEPTION")
{
throw new CriticalException();
}
}
这是业务层异常,它发生在呈现器层。只需输入截图中的值即可。
public void AddNumbers()
{
try
{
m_View.Result= Convert.ToString(AddOperations());
}
catch (Exception ex)
{
ProcessPresenterLayerException(ex);
}
}
这样我们就可以根据需求创建自定义异常了。演示项目需要 Enterprise Library DLL,因此我们需要安装 Microsoft Enterprise Library 才能运行此源代码。
我已经尽力在此提供了足够的信息来解释使用 Enterprise Library 的自定义异常的工作模型。
参考
使用此 UI 界面构建了异常处理块:Enterprise Library 异常处理块。
模型-视图-呈现器:MVP 架构。
结论
任何修正、批评和建议都非常欢迎。