我有一个名为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;
}
}
答案 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);