C#在集合上动态应用评估规则

时间:2009-01-08 02:08:40

标签: c#

我有一个固定长度的数据文件需要持久化到数据库中。我使用XML文件来定义字段的长度,并使用FieldItem类列表来存储数据。

   class FieldItem
   {
        public string ObjectName {get; set;}
        public string ObjectProperty {get; set;}
        public string ObjectValue {get; set;}

        public FieldItem()
       {
       }
   }

所以FieldItem看起来像

var fieldItem = new FieldItem
                {
                   ObjectName = "Company",
                   ObjectProperty = "Name",
                   ObjectValue = "ABC Corp."
                }

获取FieldItem列表后,我将进行反思以创建Company和其他域对象以将它们保存到数据库中。

但在将它们保存到数据库之前,我需要应用一些业务规则来验证数据行。我的数据行看起来像:

var fieldItemList = new List<FieldItem>(){
                new FieldItem {
                    ObjectName="Company",
                    ObjectProperty = "Name",
                    ObjectValue = "ABC"
                }

                new FieldItem{
                    ObjectName = "Product",
                    ObjectProperty = "ProductCode",
                    ObjectValue ="XYZ0123"

                    new FieldItem{
                    ObjectName = "Product",
                    ObjectProperty = "ProductName",
                    ObjectValue ="Christmas Tree"       
                }

                    // other FieldItem objects...

            }

例如,我的规则是检查公司是否==“ABC”和ProductCode ==“XYZ0123”。这些规则由用户创建并作为字符串存储在数据库中。我现在正在做的是使用Microsoft的System.Linq.Dynamic来评估规则,例如

string validationRule = " (ObjectName == \"Company\" And ObjectProperty=\"CompanyName\" And ObjectValue = \"ABC Corp.\") And (ObjectName == \"Product\" And ObjectProperty=\"ProductCode\" And ObjectValue = \"XYZ0123\") ";

  var query = fieldItemList.AsQuerable().Where(validationRule);

然后检查它是否有一行返回以告知该数据行是否已通过该规则。 显然,它太冗长了。你有更好的建议吗?如果我只喜欢我的规则表达式,那该怎么办?“Company.CompanyName ='ABC Corp.'和Product.ProductCode ='XYZ001'“?

5 个答案:

答案 0 :(得分:1)

考虑使用FileHelpers库将文件直接解析为常规.NET域对象。我相信这一点,结合您的Dynamic Linq方法,将为您提供您正在寻找的语法。

答案 1 :(得分:1)

RE:“如果我只喜欢我的规则表达式,我应该怎么做:”Company.CompanyName ='ABC Corp'和Product.ProductCode ='XYZ001'“?


将此用户友好的查询:“Company.CompanyName ='ABC Corp' AND Product.ProductCode ='XYZ001'”映射到对您的数据结构友好的内容(FieldItem):


“ObjectName ='Company'AND ObjectProperty ='CompanyName'ANDVarValue ='ABC Corp' OR ObjectName ='Product'AND ObjectProperty ='ProductCode'AND ObjectValue ='XYZ001'”

如何知道用户的规则是否通过规则?计算条件数,如果它匹配fieldItemList.AsQueryable()的结果计数。其中(itemFieldFriendlyQuery),则数据行根据用户的规则有效。


一些基本映射器(使用正则表达式或滚动自己的解析器以使以下代码真正有效):

public Form3()
{
    InitializeComponent();

    string userFriendlyQuery = "Company.CompanyName = 'ABC Corp' AND Product.ProductCode = 'XYZ001'";

    string[] queryConditions = userFriendlyQuery.Split(new string[]{" AND "},StringSplitOptions.None);
    int conditionsCount = queryConditions.Length;

    string itemFieldFriendlyQuery = string.Join(" OR ", 
        queryConditions.Select(condition =>
            {
                var conditionA = condition.Split(new string[] { " = " }, StringSplitOptions.None);

                var left = conditionA[0];
                var leftA = left.Split('.');

                string objectName = leftA[0];
                string objectProperty = leftA[1];

                var right = conditionA[1];

                return string.Format("ObjectName = '{0}' AND ObjectProperty = '{1}' AND ObjectValue = {2}",
                    objectName, objectProperty, right);
            }
        ).ToArray());



    MessageBox.Show(itemFieldFriendlyQuery);

    // outputs: "ObjectName = 'Company' AND ObjectProperty = 'CompanyName' AND ObjectValue = 'ABC Corp' OR ObjectName = 'Product' AND ObjectProperty = 'ProductCode' AND ObjectValue = 'XYZ001'"


    bool isValid = fieldItemList.AsQueryable().Where(itemFieldFriendlyQuery).Count() == conditionsCount;


    MessageBox.Show(isValid.ToString());
}   

答案 2 :(得分:0)

您可以将它们作为C#代码表达式存储在数据库中,然后使用CodeDom类将其解析并编译为一个类型,该类型公开将运行比较的方法。

这将创建许多临时程序集,这将是一个要管理的PITA,IMO。

相反,我仍然使用C#表示法来表达语法,但不是将其编译成代码,而是动态创建一个lambda表达式(通过Expression类),然后编译成一个获取所有参数的委托(你将这里需要一个映射机制,然后使用你创建的类型调用它。

答案 3 :(得分:0)

对于你的FieldItem类,你不能创建一个可以注入Predicate委托方法的属性,这样:

class FieldItem
{
    public string ObjectName {get; set;}
    public string ObjectProperty {get; set;}
    public string ObjectValue {get; set;}
    public Predicate<FieldItem> EvalMethod { private get; set; }

    public FieldItem()
    {
    }

    public bool Evaluate()
    {
        return EvalMethod(this);
    }
}

然后,您可以使用以下内容构建FieldItem对象:

new FieldItem { ObjectName = "SomeObject",
                ObjectProperty = "SomeProperty",
                ObjectValue = "SomeValue",
                EvalMethod = GetEvalMethodFor("SomeObject")
               }

当然,您必须拥有某种解析器(上面的GetEvalMethodFor方法)才能将validationRule字符串转换为布尔评估方法。

在您的收藏中,您可以在Evaluate()圈内拨打foreach

<强>更新

如果您需要评估列表中的每个对象,可以执行以下操作:

bool areAllItemsValid = true;

foreach (var f in fieldItemList)
{
    areAllItemsValid = areAllItemsValid && f.Evaluate();

    if (areAllItemsValid)
    {
        continue;
    }
    else
    {
        return false;
    }
}

return true;

您可以选择继承List<FieldItem>并使EvaluateAll()方法包含上述内容,或者将其声明为单独对象中的方法。

答案 4 :(得分:0)

顺便说一下:为什么要用class而不是struct?

数据的值类型,行为的引用类型。