List <t> </t>中的字符串条件

时间:2012-10-01 16:08:24

标签: c#

我有一个名为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中我需要允许根据用户输入过滤结果,所以我的客户想要一个估算值的文本框,他想在那里输入文字,如'&gt; 30k和&lt; 50k'或'&gt; 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&gt; 30000 AND EstimatedValue&lt; 60000'或'EstimatedValue&lt; 50000'

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

[编辑]

我创建了两个方法来帮助我,但我得到了异常“二进制运算符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;
        }
    }

6 个答案:

答案 0 :(得分:2)

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

E.g。 'EstimatedValue&gt; 30000 AND EstimatedValue&lt; 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);

答案 1 :(得分:0)

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

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

myList.Where(elem => new Expression(EstimatedValue.ToString() + UserExpression).Evaluate());

答案 2 :(得分:0)

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

所以

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

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

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

答案 3 :(得分: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;
    }
}

答案 4 :(得分: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)
    );

答案 5 :(得分:-1)

使用LinqToObjects

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

homeInfos.Where(x => x.EstimatedValue > 1000).Where(x => x.EstimatedValue < 10000);
相关问题