我有一个函数,该函数应该根据传递给它的Expression和/或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);
}
编译器给我以下错误:
期望的方法名称
如果我改用这个:
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);
}
然后我得到了另一个错误:
LINQ to Entities不支持LINQ表达式节点类型'Invoke'
因此,我不确定如何将lambda传递给函数,以便可以在查询中使用它。能做到吗?如果可以,怎么办?
答案 0 :(得分:1)
因此,您有一些Expression<Func<TSource, bool>>
,并且希望使用AND功能将它们合并为一个Expression<Func<TSource, bool>>
,以便可以在实体框架中将其用作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(...);
让我们实施Andso
通常情况下,哪里会是这样:
.Where(student => student.City == "Birmingham" && student.Gender == Gender.Male)
输入参数student
用作左表达式的输入和右表达式的输入。我们需要写一些东西:
采用一个类型为Student的输入参数,将其放在左边的表达式中,再放在右边的Expression中,并在两个布尔返回值之间执行AND。返回布尔结果。
为此,我们创建了一个从System.Linq.Expressions.ExpressionVisitor
派生的类。
此类代表动作:“在表达式中放入一个学生并进行计算”。该计算称为“访问表达式”。表达式的输入是一个表达式,访问的结果是另一个表达式:
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], parameter);
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));