ELAB(Exception Handling Application Block)採用策略導向異常處理:Policy = Exception Type + Handler(s)。粒度止於異常型別,無法針對同型別異常的屬性值差異採取不同處理。例如 SqlException 可能因連線失敗或違反約束拋出,ELAB 視為同一錯誤。
引入特殊 Handler 依異常屬性值動態決定執行哪些 Handler:

引入後:

<filters>
<add property="Message" value="xxx" name="filter1" typeConverter="System.ComponentModel.TypeConverter, System">
<exceptionHandlers>
<add exceptionMessage="Bar" replaceExceptionType="Artech.CustomExceptionHandlers.BarException, Artech.CustomExceptionHandlers.Demo" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ReplaceHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling" name="Replace Handler" />
</exceptionHandlers>
</add>
<add property="Message" value="yyy" name="filter2" typeConverter="System.ComponentModel.TypeConverter, System">
<exceptionHandlers>
<add exceptionMessage="Baz" replaceExceptionType="Artech.CustomExceptionHandlers.BazException, Artech.CustomExceptionHandlers.Demo" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ReplaceHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling" name="Replace Handler" />
</exceptionHandlers>
</add>
</filters>
針對 FooException,若 Message="xxx" 替換為 BarException,若 Message="yyy" 替換為 BazException。
public class FilterEntry : NamedConfigurationElement
{
[ConfigurationProperty("property", IsRequired = true)]
public string PropertyName { get; set; }
[ConfigurationProperty("value", IsRequired = true)]
public string PropertyValue { get; set; }
[ConfigurationProperty("typeConverter", DefaultValue = "")]
public string TypeConverterData { get; set; }
public TypeConverter TypeConverter
{
get
{
if (string.IsNullOrEmpty(TypeConverterData)) return new TypeConverter();
Type t = Type.GetType(TypeConverterData);
return Activator.CreateInstance(t) as TypeConverter;
}
}
[ConfigurationProperty("exceptionHandlers")]
public NameTypeConfigurationElementCollection<ExceptionHandlerData, CustomHandlerData> ExceptionHandlers { get; }
}
[Assembler(typeof(FilterableExceptionHandlerAssembler))]
public class FilterableExceptionHandlerData : ExceptionHandlerData
{
[ConfigurationProperty("filters", IsRequired = true)]
public NamedElementCollection<FilterEntry> Filters { get; set; }
}
[ConfigurationElementType(typeof(FilterableExceptionHandlerData))]
public class FilterableExceptionHandler : IExceptionHandler
{
private FilterableExceptionHandlerData _data;
public FilterableExceptionHandler(FilterableExceptionHandlerData data) => _data = data;
private IList<IExceptionHandler> GetFilteredHandler(Exception ex, FilterableExceptionHandlerData data)
{
var handlers = new List<IExceptionHandler>();
foreach (FilterEntry filter in data.Filters)
{
PropertyInfo prop = ex.GetType().GetProperty(filter.PropertyName);
if (prop == null) throw new ConfigurationErrorsException($"型別 {ex.GetType().Name} 無屬性 {filter.PropertyName}");
object val = prop.GetValue(ex, null);
string strVal = filter.TypeConverter.ConvertToString(val);
if (string.Compare(strVal, filter.PropertyValue, true) == 0)
{
foreach (ExceptionHandlerData hData in filter.ExceptionHandlers)
handlers.Add(ExceptionHandlerCustomFactory.Instance.Create(null, hData, null, null));
}
}
return handlers;
}
public Exception HandleException(Exception ex, Guid handlingInstanceId)
{
foreach (var handler in GetFilteredHandler(ex, _data))
ex = handler.HandleException(ex, handlingInstanceId);
return ex;
}
}
public class FilterableExceptionHandlerAssembler : IAssembler<IExceptionHandler, ExceptionHandlerData>
{
public IExceptionHandler Assemble(IBuilderContext context, ExceptionHandlerData config, IConfigurationSource source, ConfigurationReflectionCache cache)
=> new FilterableExceptionHandler(config as FilterableExceptionHandlerData);
}
定義 FooException、BarException、BazException,程式拋出不同 Message 的 FooException 並經 ExceptionPolicy.HandleException 處理。輸出結果證明同型別異常依屬性值分派至不同 Handler:
Exception Type: BarException; Message: Bar
Exception Type: BazException; Message: Baz

完整原始碼可供下載參考。