Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

AOP using Unity 2.0

4.89/5 (10 votes)
23 Jun 2010CPOL2 min read 41.8K  
Demonstrates AOP for Exception Handling using Unity2.0 in ASP.NET application

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:

C#
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.

C#
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:

XML
<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><!-- Removed for code brevity. You can use your listeners here. 
   The EntLib configuration tool will insert the entries here --></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).

C#
protected void Application_Start(object sender, EventArgs e)
{
    // create and populate a new Unity container from configuration
    IUnityContainer unityContainer = new UnityContainer();
    //Whatever is written below in this function can also be configured
    //via unity configuration using unityContainer.LoadConfiguration();	
	    	
    //Add Interception extension to intercept calls
    unityContainer.AddNewExtension<interception>();
    unityContainer.AddNewExtension<enterpriselibrarycoreextension>();
    unityContainer.Resolve<exceptionpolicyimpl>("ExceptionPolicy");
    //Get Policy Injection Settings from the Configuration
    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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)