我们希望对业务逻辑错误(不一定是参数错误等)执行的操作是拥有一个定义所有潜在错误类型的枚举:
/// <summary>
/// This enum is used to identify each business rule uniquely.
/// </summary>
public enum BusinessRuleId {
/// <summary>
/// Indicates that a valid body weight value of a patient is missing for dose calculation.
/// </summary>
[Display(Name = @"DoseCalculation_PatientBodyWeightMissing")]
PatientBodyWeightMissingForDoseCalculation = 1,
/// <summary>
/// Indicates that a valid body height value of a patient is missing for dose calculation.
/// </summary>
[Display(Name = @"DoseCalculation_PatientBodyHeightMissing")]
PatientBodyHeightMissingForDoseCalculation = 2,
// ...
}
这些[Display(Name = "...")]
属性定义了资源文件中用于转换错误消息的键。
另外,此文件可用作查找所有在代码中生成某种类型错误的事件的起点。
可以将业务规则的检查委派给专门的Validator类,该类产生违反业务规则的列表。
然后,我们使用自定义的Exception类型来传输违反的规则:
[Serializable]
public class BusinessLogicException : Exception {
/// <summary>
/// The Business Rule that was violated.
/// </summary>
public BusinessRuleId ViolatedBusinessRule { get; set; }
/// <summary>
/// Optional: additional parameters to be used to during generation of the error message.
/// </summary>
public string[] MessageParameters { get; set; }
/// <summary>
/// This exception indicates that a Business Rule has been violated.
/// </summary>
public BusinessLogicException(BusinessRuleId violatedBusinessRule, params string[] messageParameters) {
ViolatedBusinessRule = violatedBusinessRule;
MessageParameters = messageParameters;
}
}
后端服务调用包装在通用错误处理代码中,该代码将违反的业务规则转换为用户可读的错误消息:
public object TryExecuteServiceAction(Action a) {
try {
return a();
}
catch (BusinessLogicException bex) {
_logger.Error(GenerateErrorMessage(bex));
}
}
public string GenerateErrorMessage(BusinessLogicException bex) {
var translatedError = bex.ViolatedBusinessRule.ToTranslatedString();
if (bex.MessageParameters != null) {
translatedError = string.Format(translatedError, bex.MessageParameters);
}
return translatedError;
}
这ToTranslatedString()
是一种扩展方法enum
,可以从[Display]
属性读取资源密钥并使用ResourceManager
来翻译这些密钥。相应资源密钥的值可以包含占位符string.Format
,与提供的匹配MessageParameters
。resx文件中的条目示例:
<data name="DoseCalculation_PatientBodyWeightMissing" xml:space="preserve">
<value>The dose can not be calculated because the body weight observation for patient {0} is missing or not up to date.</value>
<comment>{0} ... Patient name</comment>
</data>
用法示例:
throw new BusinessLogicException(BusinessRuleId.PatientBodyWeightMissingForDoseCalculation, patient.Name);
使用这种方法,您可以将错误消息的生成与错误的生成分离,而无需为每种新的错误类型引入新的异常类。如果不同的前端应显示不同的消息,或者所显示的消息应取决于用户语言和/或角色等,则很有用。