1

我一直在研究规则引擎等,但我真的不知道从哪里开始。这更多是为了实验,但我想在未来实现类似的工作。基本上,我有一个应用程序,其中用户提交表单并使用多个属性填充 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;
}

当然,我知道这是一个过于简化的示例,但我遇到了很多情况,我的用户可以从我的应用程序中的这个功能中受益。我不是在寻找一个完整的解决方案,而是从哪里开始的想法。

4

2 回答 2

3

有很多方法可以解决这个问题。一个建议是利用反射本身,并允许管理员应用规则。我将保持简单,但规则将包括:

  1. 一堆属性、操作数和值
  2. 拒绝的原因。

所以让我们定义它。我将保持这个简单,只处理相等,你可以定义额外的:

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);

编辑:一些注释

  1. 我使用 alocalIsValid而不是一有机会就退出。您可以根据需要更改此设置,但其想法是它允许单个规则具有多个可否认性点。这可能是也可能不是您所希望的——但重构代码很容易,以便在单个属性比较失败时退出。

  2. 这是一个挑剔的选择,但通常 C# 样式指南规定属性不应该是驼峰式……但这完全取决于你在一天结束时:)

于 2013-08-01T18:58:28.810 回答
0

据我了解,您正在寻找某种用于业务规则的脚本系统。我发现这篇博客文章提到了一些脚本环境。

您还可以像这里提到的那样动态创建程序集:https ://stackoverflow.com/a/4181855/1229622 。

于 2013-08-01T21:45:15.323 回答