使用 Unity 2.0 的 AOP






4.89/5 (9投票s)
演示了如何在 ASP.NET 应用程序中使用 Unity 2.0 进行异常处理的 AOP
引言
本文演示了如何使用 Unity 2.0 实现 AOP。
背景
AOP 负责处理诸如异常处理/日志记录/验证等横切关注点,并将其提取到配置文件或中心位置,以便开发人员可以专注于实现业务逻辑。
问题描述
考虑来自业务逻辑组件的以下代码
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);
}
}
}
在这里,大部分代码都在进行异常处理。 上述函数演示了如何处理 Entity Framework 的 UpdateException
(例如数据库中已经存在重复的用户)并重新抛出带有消息的自定义异常(在将其记录到文件之后)。 如果发生任何其他异常,它会重新抛出任何其他类型的异常(在将其记录到文件之后)。 您通常在 Web 服务中编写此类包装异常代码以将异常转换为 SoapException
并重新抛出它。 如何将上述代码简化为如下所示的代码,并在配置文件中定义异常策略以实现异常处理和日志记录?
public class UserBO : IUserBO
{
public Boolean SaveUser(UserDTO userDTO)
{
return _userDAL.SaveUser(userDTO);
}
}
在这里,我们所做的就是编写业务逻辑。 以下是一个示例策略
- 在 DAL 层不进行异常处理。 因此,抛出的任何异常都会传播到域对象层。
- 对于域对象,如果抛出 DB 异常,则使用堆栈跟踪记录异常,抛出自定义异常
MyCustomSqlException
,将实际异常包装在其中。 如果不是 DB 异常,则使用堆栈跟踪记录异常,抛出自定义异常MyCustomBaseException
,将实际异常包装在其中。
有了此策略,我们无需在 BO 和 DAL 层编写任何 try
/catch
。 在 UI 层,我们使用 try
/catch
来确定异常的类型,以便向用户显示友好的消息。
示例异常策略
我们使用来自 Enterprise library 5.0 的 Unity 2.0。 我们可以使用 EntLib 配置工具来创建策略。 如下图所示,是 EntLib 配置设置,用于实现上述策略
<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>
上述配置很容易理解。 我们实际上已经配置了我们的策略,并附加了 Enterprise Library 中可用于日志记录和异常处理的处理程序。
下面是实现配置文件中定义的策略的代码。 也可以从配置完成,但在这里我将其显示在代码中(在这种情况下是 ASP.NET)。
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;
}
在这里,我们配置了 Unity,并注册了要拦截的 IUserBO
对象,并执行策略中配置的工作。
结论
在配置中定义策略允许更干净的代码,并可以通过配置更改策略。 将横切关注点从应用程序中移除,使开发人员能够专注于实际的业务逻辑。
历史
- 2010年6月23日:初始帖子