Introduction
This article demonstrates the creation of a custom Exception Layer using the Microsoft Enterprise Library Exception Handling block. The demo created in this
article has a Model View Presenter architecture with a custom exception layer built in it.
System Requirement
- Install Microsoft Enterprise Library .NET 2.0
- .NET Framework 2.0
Important Namespaces
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
Features of the Custom Exception Block
- The custom block partitions exceptions based on layers. E.g. Data Access Layer Exception, Presenter Layer Exception, Service Layer Exception, and so on.
- This custom Exception also targets system layer exceptions like Critical Exception. For example: When the database server is down, or a Web Service is unavailable, and so on.
- This custom exception also addresses exceptions that are handled and custom messages to be shown to the user. For example, insert duplicate records in
database, exception thrown from database, and this handled in a .NET application and bubbled up to show a user defined warning message. So this
eventually separates the warnings, errors, and exceptions. For an unhandled system error, the system is redirected to an error page. For a warning or operational
information, it is shown on the same functional screen so that normal execution is not stopped.
- This block is integrated with Model View Presenter Architecture.
Configuration Setting
The configuration settings encapsulate the various policies required for the application. The policies are:
- Global Policy: Identifies the actual exception that comes from the layers.
- Propagate Policy: This policy propagates the actual exception thrown from layers to the mainstream.
- Custom Policy: Presenter/Data Access/View/Service.
| Policy | Post Handling Action | Layer |
1 | Global | None | Project.Practice.MVP.ExceptionHandling |
2 | Propagate | NotifyReThrow | System.Exception |
3 | Custom: Presenter Layer | ThrowNewException | Project.Practice.MVP.Presenter |
4 | Custom: Data Access Layer | ThrowNewException | Project.Practice.MVP.DataAccess |
5 | Custom: View Layer | ThrowNewException | Project.Practice.MVP.Web |
Project.Practice.MVP.ExceptionHandling: Custom Exception Class
The physical representation of the exception handling class with the MVP model is as shown below.
The below class diagram depicts the picture of the exception handling class construct. All the layers
of custom exception classes are created. These classes in turn inherit the BaseException
class.
Project.Practice.MVP.ExceptionHandling: ApplicationExceptionHandler.cs
In this class, we have a method called HandleException
which implements the interface method 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
{
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);
}
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 ;
}
}
else if (exception.GetType().Equals(
typeof(CustomException.ViewLayerException)))
{
if ((CustomPage)HttpContext.Current.Handler != null)
{
((CustomPage)HttpContext.Current.Handler).Error =
"View Layer Exception:" + exception.Message;
}
}
else if (exception.GetType().Equals(
typeof(CustomException.DataAccesLayerException)))
{
if ((CustomPage)HttpContext.Current.Handler != null)
{
((CustomPage)HttpContext.Current.Handler).Error =
"DataAccess Layer Exception: "+ exception.Message;
}
}
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)
{
if ((((CustomPage)
HttpContext.Current.Handler).CurrentSession == null))
{
HttpContext.Current.Items["ERROR_MSG"] = "Access Denied";
HttpContext.Current.Server.Transfer(
"~/CriticalException.aspx", false);
}
HttpContext.Current.Server.Transfer(
"~/CriticalException.aspx", false);
}
}
}
catch (System.Threading.ThreadAbortException ex)
{
}
catch (Exception ex)
{
}
return exception;
}
#endregion
}
}
Project.Practice.MVP.ExceptionHandling: ExceptionHandleProvider.cs
This is called from the web pages where the exception gets bubbled up.
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
This is a partial class. We have a partial class named 'DataProvider
' with a different file name for each module. In this method, if the exception is a Critical Exception in
the Data Access Layer, it will be propagated across the layer to display a Critical Exception, but if there is a DataAccess exception,
then this will get propagated through the other layers to display a DataAccessLayer Exception.
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;
}
}
}
}
}
Now we have a partial class DataProvider
with a file module named
MathOperationDataProvider.cs.
DataAccess:Raise Exception
namespace Project.Practice.MVP.DataAccess
{
public partial class DataProvider
{
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()
{
}
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; ; }
}
}
Final Show
This exception is thrown or raised from the code-behind of an ASPX page.
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;
}
}
}
This exception is raised from the data access layer. Just change the error variable to test this layer.
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();
}
}
This is a business layer exception and it occurs in the Presenter Layer. Just enter the value as given in the screenshot.
public void AddNumbers()
{
try
{
m_View.Result= Convert.ToString(AddOperations());
}
catch (Exception ex)
{
ProcessPresenterLayerException(ex);
}
}
This is how we can create a custom exception as per our requirements. The demo project requires Enterprise Library DLLs,
so we need to install the Microsoft Enterprise Library to run this source code.
I have tried my best to put sufficient information here to explain the working model of custom exceptions using the Enterprise Library.
Reference
Built the Exception Handling Block using this UI Interface: Enterprise Library ExceptionHandling Block.
Model View Presenter: MVP Architecture.
Conclusion
Any corrections, criticisms, and advise are most welcome.