Ner*_*roS 8 .net c# linq lambda expression
我正在为我们这个庞大的项目创建一个更精细的过滤系统.其中一个主要谓词是能够通过字符串参数传递比较.这表现为以下形式:"> 50"或"5-10"或"<123.2"
我有什么(作为一个例子来说明)
视图模型:
TotalCost (string) (value: "<50")
Required (string) (value: "5-10")
Run Code Online (Sandbox Code Playgroud)
EF型号:
TotalCost (double)
Required(double)
Run Code Online (Sandbox Code Playgroud)
我想要使用的表达式:
model => model.Where(field => field.TotalCost.Compare(viewModel.TotalCost) && field.Required.Compare(viewModel.Required));
Run Code Online (Sandbox Code Playgroud)
我希望收到的表达:
model => model.Where(field => field.TotalCost < 50 && field.Required > 5 && field.Required < 10);
Run Code Online (Sandbox Code Playgroud)
或类似的东西
但是......我不知道从哪里开始.我把它缩小到了
public static Expression Compare<T>(this Expression<Func<T, bool>> value, string compare)
Run Code Online (Sandbox Code Playgroud)
它甚至可能不正确,但这就是我所拥有的一切.比较构建器不是问题,这很容易.困难的部分实际上是返回表达式.我从未尝试将表达式作为函数值返回.基本上我需要保留的是字段并返回一个比较表达式.
有帮助吗?:X
更新:
唉,这并不能解决我的问题.这可能是因为我在过去的23个小时里一直在努力,但我对如何将它变成一种扩展方法没有丝毫的线索.正如我所说,我想要的......基本上是一种写作方式:
var ex = new ExTest();
var items = ex.Repo.Items.Where(x => x.Cost.Compare("<50"));
Run Code Online (Sandbox Code Playgroud)
我塑造这个功能的方式(可能是完全错误的)是
public static Expression<Func<decimal, bool>> Compare(string arg)
{
if (arg.Contains("<"))
return d => d < int.Parse(arg);
return d => d > int.Parse(arg);
}
Run Code Online (Sandbox Code Playgroud)
它首先缺少"this -something- value",而我还没有弄清楚如何让它能够获得表达式输入...至于ReSharper,它建议我转换它改为布尔值...
我的脑袋此刻充满了绒毛......
更新2:
我设法找到了一种方法,可以在控制台应用程序的内存存储库中使用一段代码.我还没有尝试使用Entity Framework.
public static bool Compare(this double val, string arg)
{
var arg2 = arg.Replace("<", "").Replace(">", "");
if (arg.Contains("<"))
return val < double.Parse(arg2);
return val > double.Parse(arg2);
}
Run Code Online (Sandbox Code Playgroud)
但是,我非常怀疑这就是我所追求的
更新3:
是的,在坐下来再次查看lambda表达式之后,在最后一个答案之前,我想出了类似于下面的内容,它没有填写"Compare()"的确切要求,但它是'overload-ish'方法:
public static IQueryable<T> WhereExpression<T>(this IQueryable<T> queryable, Expression<Func<T, double>> predicate, string arg)
{
var lambda =
Expression.Lambda<Func<T, bool>>(Expression.LessThan(predicate.Body, Expression.Constant(double.Parse(50.ToString()))));
return queryable.Where(lambda);
}
Run Code Online (Sandbox Code Playgroud)
然而,尽管我的眼睛,一切看似合乎逻辑,我得到运行时例外:
System.ArgumentException was unhandled
Message=Incorrect number of parameters supplied for lambda declaration
Source=System.Core
StackTrace:
at System.Linq.Expressions.Expression.ValidateLambdaArgs(Type delegateType, Expression& body, ReadOnlyCollection`1 parameters)
at System.Linq.Expressions.Expression.Lambda[TDelegate](Expression body, String name, Boolean tailCall, IEnumerable`1 parameters)
at System.Linq.Expressions.Expression.Lambda[TDelegate](Expression body, Boolean tailCall, IEnumerable`1 parameters)
at System.Linq.Expressions.Expression.Lambda[TDelegate](Expression body, ParameterExpression[] parameters)
Run Code Online (Sandbox Code Playgroud)
这显然是罪魁祸首:
var lambda =
Expression.Lambda<Func<T, bool>>(Expression.LessThan(predicate.Body, Expression.Constant(double.Parse(50.ToString()))));
Run Code Online (Sandbox Code Playgroud)
我非常接近解决方案.如果我能从背后得到这个错误,我相信EF应该能够将其转换为SQL.否则......好吧,最后的反应可能会去.
要生成表达式,将转换为SQL(eSQL),您应该Expression手动生成.以下是GreaterThan过滤器创建的示例,其他过滤器可以使用类似的技术制作.
static Expression<Func<T, bool>> CreateGreaterThanExpression<T>(Expression<Func<T, decimal>> fieldExtractor, decimal value)
{
var xPar = Expression.Parameter(typeof(T), "x");
var x = new ParameterRebinder(xPar);
var getter = (MemberExpression)x.Visit(fieldExtractor.Body);
var resultBody = Expression.GreaterThan(getter, Expression.Constant(value, typeof(decimal)));
return Expression.Lambda<Func<T, bool>>(resultBody, xPar);
}
private sealed class ParameterRebinder : ExpressionVisitor
{
private readonly ParameterExpression _parameter;
public ParameterRebinder(ParameterExpression parameter)
{ this._parameter = parameter; }
protected override Expression VisitParameter(ParameterExpression p)
{ return base.VisitParameter(this._parameter); }
}
Run Code Online (Sandbox Code Playgroud)
以下是使用示例.(假设我们有StackEntitesEF上下文,实体设置了TestEntity实体的TestEnitities )
static void Main(string[] args)
{
using (var ents = new StackEntities())
{
var filter = CreateGreaterThanExpression<TestEnitity>(x => x.SortProperty, 3);
var items = ents.TestEnitities.Where(filter).ToArray();
}
}
Run Code Online (Sandbox Code Playgroud)
更新:为了创建复杂的表达式,您可以使用以下代码:(假设已经创建CreateLessThanExpression并CreateBetweenExpression运行了函数)
static Expression<Func<T, bool>> CreateFilterFromString<T>(Expression<Func<T, decimal>> fieldExtractor, string text)
{
var greaterOrLessRegex = new Regex(@"^\s*(?<sign>\>|\<)\s*(?<number>\d+(\.\d+){0,1})\s*$");
var match = greaterOrLessRegex.Match(text);
if (match.Success)
{
var number = decimal.Parse(match.Result("${number}"));
var sign = match.Result("${sign}");
switch (sign)
{
case ">":
return CreateGreaterThanExpression(fieldExtractor, number);
case "<":
return CreateLessThanExpression(fieldExtractor, number);
default:
throw new Exception("Bad Sign!");
}
}
var betweenRegex = new Regex(@"^\s*(?<number1>\d+(\.\d+){0,1})\s*-\s*(?<number2>\d+(\.\d+){0,1})\s*$");
match = betweenRegex.Match(text);
if (match.Success)
{
var number1 = decimal.Parse(match.Result("${number1}"));
var number2 = decimal.Parse(match.Result("${number2}"));
return CreateBetweenExpression(fieldExtractor, number1, number2);
}
throw new Exception("Bad filter Format!");
}
Run Code Online (Sandbox Code Playgroud)
C# 编译器的一个乍一看神奇的功能可以为您完成繁重的工作。你可能知道你可以这样做:
Func<decimal, bool> totalCostIsUnder50 = d => d < 50m;
Run Code Online (Sandbox Code Playgroud)
也就是说,使用 lambda 表达式来分配Func. 但是您知道吗,您也可以这样做:
Expression<Func<decimal, bool>> totalCostIsUnder50Expression = d => d < 50m;
Run Code Online (Sandbox Code Playgroud)
也就是说,使用 lambda 表达式来分配一个Expression表示Func? 它很整洁。
鉴于你说
比较构建器不是问题,这很容易。困难的部分实际上是返回表达式
我假设你可以在这里填空;假设我们将 `"<50" 传递给:
Expression<Func<decimal, bool>> TotalCostCheckerBuilder(string criterion)
{
// Split criterion into operator and value
// when operator is < do this:
return d => d < value;
// when operator is > do this:
return d => d > value;
// and so on
}
Run Code Online (Sandbox Code Playgroud)
最后,要将您的Expressions 与&&(并且仍然有Expression)组合在一起,请执行以下操作:
var andExpression = Expression.And(firstExpression, secondExpression);
Run Code Online (Sandbox Code Playgroud)