1

我有一个名为HomeInfo

public class HomeInfo
{
   public int ID {get;set;}
   public string OwnerName {get;set;}
   public string Address {get;set;}
   public int EstimatedValue {get;set;}
}

我从服务器获取数据并将其添加到List<HomeInfo> listHomeInfo

现在在我的 GUI 中,我需要允许根据用户输入过滤结果,所以我的客户想要一个用于估计值的文本框,他想在那里输入文本,如“>30k 和 <50k”或“>50k”,我解析并转换了这些类的值和创建的对象

public class ExpressionValue
{
    public float? FirstDigit { get; set; }
    /// <summary>
    /// >, >=, <,<=
    /// </summary>
    public string FirstExpCondition { get; set; }
    /// <summary>
    /// OR, AND
    /// </summary>
    public string ConditionOperator { get; set; }
    public float SecondDigit { get; set; }
    public string SecondExpCondition { get; set; }
}

使用 ExpressionValue 对象,我能够创建适当的条件字符串。现在我可以创建一个条件字符串,例如“EstimatedValue > 30000 AND EstimatedValue < 60000”或“EstimatedValue < 50000”

我不知道如何在“List listHomeInfo”上有效地应用此条件,因为据我所知List<T>.Where()不支持字符串条件。我知道一种解决方法是将列表转换为 DataTable 并使用Select(string expression)方法然后转换DataRow[]List<HomeInfo>,但我认为可能有更好的方法来实现这一点。

[编辑]

我创建了两种方法来帮助我,但我得到了异常“二进制运算符 GreaterThan 没有为类型 'System.Single' 和 'System.Double' 定义。” 创建 BinaryExpression 时。

public static Expression<Func<T, bool>> ParseExpressionCondition<T>(string expression, string fieldName)
    {
        try
        {
            string decimalNumRegex = @"\d+(\.\d{1,2})?";
            List<string> matchPatterns = new List<string>() { ">=", ">", "<=", "<" };
            ExpressionValue expValue = new ExpressionValue();
            Dictionary<string, string> conditions = new Dictionary<string, string>();
            var parameter = Expression.Parameter(typeof(T), typeof(T).ToString());
            //var lhs = Expression.GreaterThan(Expression.Property(parameter, "EstimatedValue"), Expression.Constant(30000));
            BinaryExpression lhs = null, rhs = null;
            object objectValue = null;
            string condOperator = null;
            foreach (string pattern in matchPatterns)
            {
                Match match = Regex.Match(expression, pattern + decimalNumRegex);

                if (match.Success)
                {
                    //get digit part
                    double digit = double.Parse(Regex.Match(match.Value, decimalNumRegex).Value);
                    if (!expValue.FirstDigit.HasValue)
                    {
                        objectValue = digit;
                        condOperator = match.Value.Replace(digit.ToString(), "");
                        lhs = GetBinaryExpression(parameter, fieldName, objectValue, condOperator);
                    }
                    else
                    {
                        objectValue = digit;
                        condOperator = match.Value.Replace(digit.ToString(), "");
                        rhs = GetBinaryExpression(parameter, fieldName, objectValue, condOperator);
                    }
                }
            }

            if (expression.ToLower().Contains("and"))
                return Expression.Lambda<Func<T, bool>>(Expression.And(lhs, rhs), parameter);
            else if (expression.ToLower().Contains("or"))
                return Expression.Lambda<Func<T, bool>>(Expression.Or(lhs, rhs), parameter);


            return null;
        }
        catch (Exception ex)
        {
            Logger.WriteLog(ex);
            throw ex;
        }
    }

    private static BinaryExpression GetBinaryExpression(ParameterExpression paraExp, string fieldName, object expressionValue, string conditionOperator)
    {
        try
        {
            BinaryExpression binExp = null;
            MemberExpression expressionLeft = Expression.Property(paraExp, fieldName);
            Expression expressionRight = Expression.Constant(expressionValue );
            switch (conditionOperator)
            {
                case ">":
                    binExp = Expression.GreaterThan(expressionLeft, expressionRight);
                    break;
                case ">=":
                    binExp = Expression.GreaterThanOrEqual(expressionLeft, expressionRight);
                    break;
                case "<":
                    binExp = Expression.LessThan(expressionLeft, expressionRight);
                    break;
                case "<=":
                    binExp = Expression.LessThanOrEqual(expressionLeft, expressionRight);
                    break;
            }
            return binExp;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }
4

6 回答 6

2

假设您已经在某种程度上实现了解析逻辑,我建议您生成一个表达式树(而不是使用您自己的自定义ExpressionValue类)。

例如 'EstimatedValue > 30000 AND EstimatedValue < 60000' 可以变成如下形式的表达式树:

var parameter = Expression.Parameter(typeof(HomeInfo), "homeInfo");
var lhs = Expression.GreaterThan(Expression.Property(parameter, "EstimatedValue"), Expression.Constant(30000));
var rhs = Expression.LessThan(Expression.Property(parameter, "EstimatedValue"), Expression.Constant(60000));
var expression = Expression.Lambda<Func<HomeInfo, bool>>(Expression.AndAlso(lhs, rhs), parameter);

然后可以使用生成的表达式树查询该列表,如下所示:

var results = listHomeInfo.AsQueryable().Where(expression);
于 2012-10-01T16:19:52.780 回答
0

不要重新发明轮子:NCalc已经做了这种事情。

使用名为的变量EstimatedValue和用户定义的表达式UserExpression,在 NCalc 中您可以:

myList.Where(elem => new Expression(EstimatedValue.ToString() + UserExpression).Evaluate());
于 2012-10-01T16:16:06.973 回答
0

在你的位置上,我会创建一个迷你规则引擎。

所以

public abstract class ExpressionBase {
 public float value {get;set;}
}

public class GreaterThanExpression : ExpressionBase {}
public class LessThanExpression : ExpressionBase {}

现在,当您解析输入的字符串时,您可以构建输入表达式的列表,然后按照您想要的顺序将它们应用于 IQueryable。

于 2012-10-01T16:16:39.487 回答
0

写一个LINQ扩展方法....

public static IEnumerable<HomeInfo> PassesExpression(this IEnumerable<HomeInfo> homes, ExpressionValue expression)
{
    foreach(HomeInfo home in homes)
    {
        bool one, two;

        if(expression.FirstExpCondition == '>')
            one = (home.EstimatedValue > expression.FirstDigit);
        else if(expression.FirstExpCondition == '>=')
            one = (home.EstimatedValue >= expression.FirstDigit);
        else if(expression.FirstExpCondition == '<')
            one = (home.EstimatedValue < expression.FirstDigit);
        else if(expression.FirstExpCondition == '<=')
            one = (home.EstimatedValue <= expression.FirstDigit);

        if(expression.SecondExpCondition == '>')
            two = (home.EstimatedValue > expression.SecondDigit);
        else if(expression.SecondExpCondition == '>=')
            two = (home.EstimatedValue >= expression.SecondDigit);
        else if(expression.SecondExpCondition == '<')
            two = (home.EstimatedValue < expression.SecondDigit);
        else if(expression.SecondExpCondition == '<=')
            two = (home.EstimatedValue <= expression.SecondDigit);

        if((expression.ConditionOperator == 'OR' && (one || two)) || (expression.ConditionOperator == 'AND' && (one && two)))
            yield return home;
    }
}
于 2012-10-01T16:22:59.553 回答
0

我通常有两个用于值范围的文本框。一个为最小值,一个为最大值。如果不需要限制,它们可以为空

int? min = null
int? max = null;
int i;

if (Int32.TryParse(txtMin.Text, out i) min = i;
if (Int32.TryParse(txtMax.Text, out i) max = i;

string name = txtName.Text;

使用这些定义,您可以动态组合 where 子句

IEnumerable<HomeInfo> result = list;
if (min.HasValue) result = result.Where(h => h.EstimatedValue >= min.Value);
if (max.HasValue) result = result.Where(h => h.EstimatedValue <= max.Value);
if (name != "")
    result = result.Where(
        h => h.OwnerName.StartsWith(name, StringComparison.OrdinalIgnoreCase)
    );
于 2012-10-01T16:23:55.263 回答
-1

使用 LinqToObjects

List<HomeInfo> homeInfos = new List<HomeInfo>();

homeInfos.Where(x => x.EstimatedValue > 1000).Where(x => x.EstimatedValue < 10000);
于 2012-10-01T16:15:46.443 回答