允许用户在C#中定义规则的最佳方法
本文关键字:定义 规则 方法 最佳 许用户 用户 | 更新日期: 2023-09-27 18:26:03
我一直在研究规则引擎之类的东西,但我真的不知道从哪里开始。这更多的是为了实验,但我想在未来的工作中实现这样的东西。基本上,我有一个应用程序,其中用户提交一个表单,并用几个属性填充POCO对象。我希望应用程序的管理员能够根据所述对象的属性定义规则,并将它们存储在关系数据库中。当提交表单时,我会根据用户定义的规则做出决定。例如,管理员可以进入应用程序并定义如下规则:
if (typeID == 4 && request.benchMarkScore < 10) {
request.denied = true;
request.denyReasons.Add("Score too low for this product");
}
这是我的POCO对象示例:
class Request
{
public int benchMarkScore { get; set; }
public int typeID { get; set; }
public double rate { get; set; }
public bool isEligable { get; set; }
public bool denied { get; set; }
public List<string> denyReasons { get; set; }
public Dictionary<string, double> adjustments;
}
当然,我知道这是一个过于简化的例子,但我遇到了许多情况,我的用户可以从我的应用程序中的这一功能中受益。我不是在寻找一个完整的解决方案,而是一个从哪里开始的想法。
有很多方法可以实现这一点。一个建议是利用反射本身,并允许管理员应用规则。我将保持简单,但一个规则将包括:
- 一组属性、操作数和值
- 拒绝的原因
让我们来定义一下。我将保持简单,只处理等式,您可以定义其他等式:
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);
编辑:一些注释
我使用
localIsValid
,而不是一有机会就退出。如果你愿意,你可以改变这一点,但其想法是,它允许一条规则有多个可否认点。这可能是你想要的,也可能不是你想要的——但重构代码很容易,这样它就可以在单个属性比较失败的时候摆脱困境。这是一个挑剔的地方,但通常C#风格的指导方针规定属性不应该是骆驼帽。。。但这完全取决于你:)
据我所知,您正在寻找某种用于业务规则的脚本系统。我发现这篇博客文章中提到了一些脚本环境。
您也可以像这里提到的那样动态创建程序集:https://stackoverflow.com/a/4181855/1229622.