Introduction
This article demonstrates how to implement AOP using Unity 2.0.
Background
AOP takes care of removing cross cutting concerns like Exception Handling / Logging / Validations, etc., and extracting it in a configuration file or a central place so that developers can focus on implementing Business logic.
Problem Description
Consider the below code from a Business Logic component:
public class UserBO : IUserBO
{
public Boolean SaveUser(UserDTO userDTO)
{
try
{
return _userDAL.SaveUser(userDTO);
}
catch (UpdateException uex)
{
logger.Log(uex);
throw new MyCustomSqlException("DB Exception occurred", uex);
}
catch (Exception ex)
{
logger.Log(ex);
throw new MyCustomBaseException(ex.Message, ex);
}
}
}
Here, the majority of code is doing Exception handling. The above function demonstrates handling an Entity framework UpdateException
(like duplicate user already in database) and rethrowing a custom exception with a message (after logging it to a file). In case of any other exception, it rethrows any other type of exception (after logging it to a file). You often write such wrapping exception codes in Webservices to convert a exception to SoapException
and rethrowing it. How about stripping the above code to the one shown below and defining an Exception policy in a configuration file to achieve Exception handling and logging.
public class UserBO : IUserBO
{
public Boolean SaveUser(UserDTO userDTO)
{
return _userDAL.SaveUser(userDTO);
}
}
Here all we are doing is writing the business logic. Below is a sample policy:
- No exception handling at DAL layer. So, any exception thrown is propagated to Domain object layer.
- For Domain object, if DB Exception is thrown, log the exception with stack trace, throw a custom exception
MyCustomSqlException
, wrapping the actual exception in it. If not a DB Exception, then log the exception with stack trace, throw a custom exception MyCustomBaseException
, wrapping the actual exception in it.
With this policy in place, we do not write any try
/catch
in BO and DAL layers. At UI layer, we use try
/catch
to identify the type of exception to show user friendly message to the users.
Sample Exception Policy
We are using Unity 2.0 from Enterprise library 5.0. We can use EntLib configuration tool to create a policy. The below image shows the EntLib configuration setting to implement policy described above:
<configSections>
<section name="policyInjection" type="Microsoft.Practices.EnterpriseLibrary.
PolicyInjection.Configuration.PolicyInjectionSettings, Microsoft.
Practices.EnterpriseLibrary.PolicyInjection, Version=5.0.414.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
<section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.
Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.
Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
requirePermission="true" />
<section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.
ExceptionHandling.Configuration.ExceptionHandlingSettings,
Microsoft.Practices.EnterpriseLibrary.ExceptionHandling,
Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
requirePermission="true" />
<section name="unity" type="Microsoft.Practices.Unity.Configuration.
UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
</configSections>
<policyInjection>
<policies>
<add name="MyPolicy">
<matchingRules>
<add type="Microsoft.Practices.EnterpriseLibrary.
PolicyInjection.MatchingRules.TypeMatchingRule,
Microsoft.Practices.EnterpriseLibrary.PolicyInjection,
Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
name="Type Matching Rule">
<matches>
<add match="MyNamespace.IUserBO" />
</matches>
</add>
</matchingRules>
<handlers>
<add type="Microsoft.Practices.EnterpriseLibrary.
ExceptionHandling.PolicyInjection.ExceptionCallHandler,
Microsoft.Practices.EnterpriseLibrary.ExceptionHandling,
Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
exceptionPolicyName="ExceptionPolicy" order="1"
name="Exception Handling Call Handler" />
</handlers>
</add>
</policies>
</policyInjection>
<loggingConfiguration></loggingConfiguration>
<exceptionHandling>
<exceptionPolicies>
<add name="ExceptionPolicy">
<exceptionTypes>
<add name="UpdateException" type="System.Data.UpdateException,
System.Data.Entity, Version=3.5.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
postHandlingAction="ThrowNewException">
<exceptionHandlers>
<add name="Wrap Handler" type="Microsoft.Practices.EnterpriseLibrary.
ExceptionHandling.WrapHandler, Microsoft.Practices.EnterpriseLibrary.
ExceptionHandling, Version=5.0.414.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"
exceptionMessage="Exception while updating entity"
wrapExceptionType="MyNamespace.MyCustomSQLException,
Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<add name="Wrap Handler Logging Exception Handler"
type="Microsoft.Practices.EnterpriseLibrary.
ExceptionHandling.Logging.LoggingExceptionHandler,
Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging,
Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
logCategory="General" eventId="100" severity="Error"
title="SQL Exception Handling"
formatterType="Microsoft.Practices.EnterpriseLibrary.
ExceptionHandling.TextExceptionFormatter,
Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"
priority="0" />
</exceptionHandlers>
</add>
<add name="Exception" type="System.Exception, mscorlib,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
postHandlingAction="NotifyRethrow">
<exceptionHandlers>
<add name="Logging Exception Handler"
type="Microsoft.Practices.EnterpriseLibrary.
ExceptionHandling.Logging.LoggingExceptionHandler,
Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging,
Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
logCategory="General" eventId="100" severity="Error"
title="RepSetup Exception Handling"
formatterType="Microsoft.Practices.EnterpriseLibrary.
ExceptionHandling.TextExceptionFormatter,
Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"
priority="0" />
</exceptionHandlers>
</add>
</exceptionTypes>
</add>
</exceptionPolicies>
</exceptionHandling>
The above configuration is quite understandable. We have actually configured our policy and attached handlers available in Enterprise Library for Logging and Exception Handling.
Here below is the code to implement the Policy defined in configuration file. This can be done from configuration also, but here I am showing it in code (ASP.NET in this case).
protected void Application_Start(object sender, EventArgs e)
{
IUnityContainer unityContainer = new UnityContainer();
unityContainer.AddNewExtension<interception>();
unityContainer.AddNewExtension<enterpriselibrarycoreextension>();
unityContainer.Resolve<exceptionpolicyimpl>("ExceptionPolicy");
IConfigurationSource configSource = ConfigurationSourceFactory.Create();
PolicyInjectionSettings policyInjectionsettings =
(PolicyInjectionSettings)configSource.GetSection
(PolicyInjectionSettings.SectionName);
unityContainer.RegisterType<iuserbo,>();
unityContainer.Configure<interception>().SetInterceptorFor<iuserbo>
(new TransparentProxyInterceptor());
if (policyInjectionsettings != null)
{
policyInjectionsettings.ConfigureContainer(unityContainer, configSource);
}
Application["UnityContainer"] = unityContainer;
}
Here we have configured Unity and registered IUserBO
objects to be intercepted and perform the work configured in the policy.
Conclusion
Defining policy in config allows cleaner code and enables changing the policy via configuration. Moving cross cutting concerns from application enables developers to focus on actual business logic.
History
- 23rd June, 2010: Initial post