带有表达式的 EF LINQ 查询:预期方法名称 - 如何将表达式传递到要在 EF 查询中使用的函数中?
EF LINQ query with Expression: method name expected - How can I pass an Expression into function to be used in EF query?
我有一个函数应该 return 与基于传递给它的表达式 and/or lambda 的 EF LINQ 查询不同的信息。
这是我的代码:
public static ObservableCollection<SI> GetDisplayStratsByJurisd(string jurisd, short Year,
Expression<Func<string, bool>> lambStat)
{
var ctx = new MI_Entities(server, database);
var strats = from y in ctx.SIset.AsNoTracking()
where y.Jurisd == jurisd && y.Year_ID == Year && lambStat(y.Status)
select y;
return new ObservableCollection<SI>(strats);
}
编译器给我以下错误:
Method name expected
如果我改用这个:
public static ObservableCollection<SI> GetDisplayStratsByJurisd(string jurisd, short Year,
Expression<Func<string, bool>> lambStat)
{
var ctx = new MI_Entities(server, database);
Func<string, bool> bob = lambStat.Compile();
var strats = from y in ctx.SIset.AsNoTracking()
where y.Jurisd == jurisd && y.Year_ID == Year && bob(y.Status)
select y;
return new ObservableCollection<SI>(strats);
}
然后我得到一个不同的错误:
The LINQ expression node type 'Invoke' is not supported in LINQ to Entities
所以,我不确定如何将 lambda 传递给函数以便它可以在查询中使用。这可以做到吗?如果可以,怎么做?
所以你有一些 Expression<Func<TSource, bool>>
,你想使用 AND 功能将它们组合成一个 Expression<Func<TSource, bool>>
,这样你就可以在 entity framework.[=22= 中使用它 AsQueryable ]
如果能以类似 LINQ 的方式使用它会很好,所以我们可以将它放在 Linq 语句的串联之间。
让我们创建一些扩展函数。
// A function that takes two Expression<Func<TSource, bool>> and returns the AND expression
static Expression<Func<TSource, bool>> AndAlso<TSource> (
this Expression<Func<TSource, bool>> x,
Expression<Func<TSource, bool>> y)
{
// TODO implement
}
用法:
Expression<Func<Student, bool>> expr1 = student => student.City == "Birmingham";
Expression<Func<Student, bool>> expr2 = student => student.Gender == Gender.Male;
Expression<Func<Student, bool>> exprAND = expr1.AndAlso(expr2);
var brummyMaleStudents = dbContext.Students.Where(exprAnd).Select(...);
让我们实施 AndAlso
通常 Where 会是这样的:
.Where(student => student.City == "Birmingham" && student.Gender == Gender.Male)
输入参数student
作为左表达式的输入,作为右表达式的输入。我们需要有这样的东西:
Take one input parameter of type Student, put it in the left expression, and in the right Expression and perform an AND between the two Boolean return values. Return the Boolean result.
为此,我们创建了一个 class 派生自 System.Linq.Expressions.ExpressionVisitor
。
这个class代表动作:“把一个学生放在表达式中并计算它”。这种计算称为“访问表达式”。表达式的输入是一个表达式,访问的结果是另一个表达式:
internal class ReplaceExpressionVisitor : ExpressionVisitor
{
private readonly Expression oldValue;
private readonly Expression newValue;
public ReplaceExpressionVisitor(ParameterExpression oldValue,
ParameterExpression newValue)
{
this.oldValue = oldValue;
this.newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == this.oldValue)
{ // "my" expression is visited
return this.newValue;
}
else
{ // not my Expression, I don't know how to Visit it, let the base class handle this
return base.Visit(node);
}
}
}
现在我们已经创建了表达式 visitor,我们可以实现 AndAlso:
static Expression<Func<TSource, bool>> AndAlso<TSource>(
this Expression<Func<TSource, bool>> expr1,
Expression<Func<TSource, bool>> expr2)
{
// Create one expression that represent expr1 && expr2
// the input of expr1 is a TSource,
// the input of expr2 is a TSource,
// so the input of expr1 && expr2 is a TSource:
ParameterExpression inputParameter = Expression.Parameter(typeof(TSource));
// Visit the left part of the AND:
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], inputParameter)
var left = leftVisitor.Visit(expr1.Body);
// Visit the right part of the AND:
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], inputParameter);
var right = rightVisitor.Visit(expr2.Body);
// Combine left and right with a binary expression representing left && right:
var andExpression = Expression.AndAlso(left, right);
// return the lambda expression that takes one Tsource as input and returns the AND:
var lambda = Expression.Lambda<Func<TSource, bool>>(andExpression, new[] {parameter});
return lambda;
}
用法:
Expression<Func<Student, bool>> expr1 = student => student.City == "Birmingham";
Expression<Func<Student, bool>> expr2 = student => student.Gender == Gender.Male;
var brummyMaleStudents = dbContext.Students.Where(expr1.AndAlso(expr2));
另一个例子:
IQueryable<int> numbers = Enumerable.Range(0, 100).Asqueryable();
// two expressions: one that filters 0, 2, 4, 6, 8, ...
// and one that filters 0, 3, 6, 9, 12, ...
Expression<Func<int, bool>> exprEvenNumbers = x => x % 2 == 0;
Expression<Func<int, bool>> exprTrifolds = x => x % 3 == 0;
// the AND of these two expressions should filter 0, 6, 12, 18, ...
Expression<Func<int, bool>> exprSixfolds = exprEvenNumbers.AndAlso(exprTrifolds);
// Test this
IQueryable<int> sixfolds = numbers.Where(exprSixfolds);
IEnumerable<int> results = sixfolds.Take(10).AsEnumerable();
foreach (int i in results)
{
Console.WriteLine(i);
}
这将打印数字 0、6、12、18,...
我有一个函数应该 return 与基于传递给它的表达式 and/or lambda 的 EF LINQ 查询不同的信息。
这是我的代码:
public static ObservableCollection<SI> GetDisplayStratsByJurisd(string jurisd, short Year,
Expression<Func<string, bool>> lambStat)
{
var ctx = new MI_Entities(server, database);
var strats = from y in ctx.SIset.AsNoTracking()
where y.Jurisd == jurisd && y.Year_ID == Year && lambStat(y.Status)
select y;
return new ObservableCollection<SI>(strats);
}
编译器给我以下错误:
Method name expected
如果我改用这个:
public static ObservableCollection<SI> GetDisplayStratsByJurisd(string jurisd, short Year,
Expression<Func<string, bool>> lambStat)
{
var ctx = new MI_Entities(server, database);
Func<string, bool> bob = lambStat.Compile();
var strats = from y in ctx.SIset.AsNoTracking()
where y.Jurisd == jurisd && y.Year_ID == Year && bob(y.Status)
select y;
return new ObservableCollection<SI>(strats);
}
然后我得到一个不同的错误:
The LINQ expression node type 'Invoke' is not supported in LINQ to Entities
所以,我不确定如何将 lambda 传递给函数以便它可以在查询中使用。这可以做到吗?如果可以,怎么做?
所以你有一些 Expression<Func<TSource, bool>>
,你想使用 AND 功能将它们组合成一个 Expression<Func<TSource, bool>>
,这样你就可以在 entity framework.[=22= 中使用它 AsQueryable ]
如果能以类似 LINQ 的方式使用它会很好,所以我们可以将它放在 Linq 语句的串联之间。
让我们创建一些扩展函数。
// A function that takes two Expression<Func<TSource, bool>> and returns the AND expression
static Expression<Func<TSource, bool>> AndAlso<TSource> (
this Expression<Func<TSource, bool>> x,
Expression<Func<TSource, bool>> y)
{
// TODO implement
}
用法:
Expression<Func<Student, bool>> expr1 = student => student.City == "Birmingham";
Expression<Func<Student, bool>> expr2 = student => student.Gender == Gender.Male;
Expression<Func<Student, bool>> exprAND = expr1.AndAlso(expr2);
var brummyMaleStudents = dbContext.Students.Where(exprAnd).Select(...);
让我们实施 AndAlso
通常 Where 会是这样的:
.Where(student => student.City == "Birmingham" && student.Gender == Gender.Male)
输入参数student
作为左表达式的输入,作为右表达式的输入。我们需要有这样的东西:
Take one input parameter of type Student, put it in the left expression, and in the right Expression and perform an AND between the two Boolean return values. Return the Boolean result.
为此,我们创建了一个 class 派生自 System.Linq.Expressions.ExpressionVisitor
。
这个class代表动作:“把一个学生放在表达式中并计算它”。这种计算称为“访问表达式”。表达式的输入是一个表达式,访问的结果是另一个表达式:
internal class ReplaceExpressionVisitor : ExpressionVisitor
{
private readonly Expression oldValue;
private readonly Expression newValue;
public ReplaceExpressionVisitor(ParameterExpression oldValue,
ParameterExpression newValue)
{
this.oldValue = oldValue;
this.newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == this.oldValue)
{ // "my" expression is visited
return this.newValue;
}
else
{ // not my Expression, I don't know how to Visit it, let the base class handle this
return base.Visit(node);
}
}
}
现在我们已经创建了表达式 visitor,我们可以实现 AndAlso:
static Expression<Func<TSource, bool>> AndAlso<TSource>(
this Expression<Func<TSource, bool>> expr1,
Expression<Func<TSource, bool>> expr2)
{
// Create one expression that represent expr1 && expr2
// the input of expr1 is a TSource,
// the input of expr2 is a TSource,
// so the input of expr1 && expr2 is a TSource:
ParameterExpression inputParameter = Expression.Parameter(typeof(TSource));
// Visit the left part of the AND:
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], inputParameter)
var left = leftVisitor.Visit(expr1.Body);
// Visit the right part of the AND:
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], inputParameter);
var right = rightVisitor.Visit(expr2.Body);
// Combine left and right with a binary expression representing left && right:
var andExpression = Expression.AndAlso(left, right);
// return the lambda expression that takes one Tsource as input and returns the AND:
var lambda = Expression.Lambda<Func<TSource, bool>>(andExpression, new[] {parameter});
return lambda;
}
用法:
Expression<Func<Student, bool>> expr1 = student => student.City == "Birmingham";
Expression<Func<Student, bool>> expr2 = student => student.Gender == Gender.Male;
var brummyMaleStudents = dbContext.Students.Where(expr1.AndAlso(expr2));
另一个例子:
IQueryable<int> numbers = Enumerable.Range(0, 100).Asqueryable();
// two expressions: one that filters 0, 2, 4, 6, 8, ...
// and one that filters 0, 3, 6, 9, 12, ...
Expression<Func<int, bool>> exprEvenNumbers = x => x % 2 == 0;
Expression<Func<int, bool>> exprTrifolds = x => x % 3 == 0;
// the AND of these two expressions should filter 0, 6, 12, 18, ...
Expression<Func<int, bool>> exprSixfolds = exprEvenNumbers.AndAlso(exprTrifolds);
// Test this
IQueryable<int> sixfolds = numbers.Where(exprSixfolds);
IEnumerable<int> results = sixfolds.Take(10).AsEnumerable();
foreach (int i in results)
{
Console.WriteLine(i);
}
这将打印数字 0、6、12、18,...