有很多方法可以解决这个问题。一个建议是利用反射本身,并允许管理员应用规则。我将保持简单,但规则将包括:
- 一堆属性、操作数和值
- 拒绝的原因。
所以让我们定义它。我将保持这个简单,只处理相等,你可以定义额外的:
public enum Operand
{
Equals
}
现在,我们可以定义一个名为IRule
. 我正在定义一个接口,以便将来您可以放入特殊的、更复杂的规则。
public interface IRule<TPOCO> where TPOCO : class
{
bool IsValid(TPOCO poco);
}
现在我们将定义我们的 Rule 类(注意:这不处理索引属性):
public class PropertyCompareRule : IRule<Request>
{
private sealed class PropertyCompare
{
public string PropertyName {get; set; }
public Operand Operand {get; set; }
public object Value {get; set;}
public string Reason {get; set; }
}
private List<PropertyCompare> _comparers = new List<PropertyCompare>();
public bool IsValid(Request poco)
{
bool isValid = true; // let's be optimistic!
PropertyInfo[] properties = poco.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where((property) => property.GetIndexParameters().Length == 0 && property.CanRead).ToArray();
foreach(var property in properties)
{
foreach(var comparer in _comparers)
{
bool localIsValid;
if(comparer.PropertyName == property.Name)
{
object val = property.GetValue(poco, null);
switch(comparer.Operand)
{
case Operand.Equals:
{
localIsValid = object.Equals(val, property.Value);
break;
}
}
if(!localIsValid)
{
poco.denyReasons.Add(comparer.Reason);
isValid = false;
}
}
}
}
return isValid;
}
public void AddComparer(string propertyName, Operand op, object value, string reason)
{
_comparers.Add(new PropertyCompare() { PropertyName = propertyName, Operand = op, Value = value, Reason = reason });
}
}
将属性名称、操作数和值详细信息持久保存在数据库或其他此类存储中对您来说并不难。假设我们充实了上面的枚举,我们可以想象这样做:
PropertyCompareRule rule = new PropertyCompareRule();
rule.AddComparer("typeID", Operand.Equal, 4, "Reason 1");
rule.AddComparer("benchMarkScore", Operand.LessThan, 10, "Reason 2");
bool valid = rule.IsValid(somePocoInstance);
编辑:一些注释
我使用 alocalIsValid
而不是一有机会就退出。您可以根据需要更改此设置,但其想法是它允许单个规则具有多个可否认性点。这可能是也可能不是您所希望的——但重构代码很容易,以便在单个属性比较失败时退出。
这是一个挑剔的选择,但通常 C# 样式指南规定属性不应该是驼峰式……但这完全取决于你在一天结束时:)