创建的谓词在 Linq to Entity Framework Where 子句中不起作用
Created predicate doesn't work in Linq to Entity Framework Where clause
我想将谓词构建逻辑移动到基础 class 中,但收到错误消息“LINQ to Entities 不支持 LINQ 表达式节点类型 'Invoke'”。我希望能够连接或有条件地链接谓词中的表达式。
我希望能够传递调用者所独有的谓词部分,即使用的 属性 名称(GetFilterPredicate 将成为将对具有不同 属性 名称并保存相关值的类型进行操作的通用例程。
protected Expression<Func<MyEntity, bool>> GetFilterPredicate(
PagingParameters pagingParameters,
Func<MyEntity, DateTime?> terminationDate,
Func<MyEntity, string> entityID,
Func<MyEntity, string> name
)
{
Expression<Func<MyEntity, bool>> validFilter = x => true;
if (pagingParameters.Active == true)
{
validFilter = x => terminationDate(x) > DateTime.Now;
}
///more conditions added here
return validFilter;
}
protected List<MyEntity> Query(IQueryable<MyEntity> source)
{
var filters = GetFilterPredicate(
new PagingParameters() { Active = true }
, i => i.TerminationDate
, i => i.EntityID
, i => i.Name
);
return source.Where(filters).AsNoTracking().ToList<MyEntity>();
}
您无法使用委托构建新表达式 terminationDate
您需要将 terminationDate
更改为表达式并使用它手动构建新表达式。
protected static Expression<Func<MyEntity, bool>> GetFilterPredicate(
PagingParameters pagingParameters,
Expression<Func<MyEntity, DateTime?>> terminationDate,
Expression<Func<MyEntity, string>> entityID,
Expression<Func<MyEntity, string>> name
)
{
Expression<Func<MyEntity, bool>> validFilter = x => true;
// We need to replace the parameter for all expressions with
// a common single parameter. I used the parameter for the default
// filter but a new Parameter expression would have worked as well.
// If you don't do this you will get an error (unbound parameter or something like that ) because the parameter
// used in the expressions (terminationDate, entityID) will be
// different then the parameter used for the new validFilter expression
var parameterReplacer = new ReplaceVisitor
{
NewParameter = validFilter.Parameters.First()
};
if (pagingParameters.Active == true)
{
validFilter = Expression.Lambda<Func<MyEntity, bool>>(
Expression.GreaterThan
(
parameterReplacer.Visit(terminationDate.Body),
Expression.Convert(Expression.Property(null, typeof(DateTime), "Now"), typeof(DateTime?))
),
parameterReplacer.NewParameter
);
}
// existing filter && x.EntityId != "A"
validFilter = Expression.Lambda<Func<MyEntity, bool>>(
Expression.And(
validFilter.Body,
Expression.NotEqual
(
parameterReplacer.Visit(entityID.Body),
Expression.Constant("A")
)
),
parameterReplacer.NewParameter
);
return validFilter;
}
/// <summary>
/// Simple Parameter Replacer, will replace the any parameter with the new
/// parameter. You will need to refine this if your expressions have nested
/// lambda, in that you will need to only replace the top most lambda
/// parameter, but for simple expressions it will work fine.
/// </summary>
class ReplaceVisitor : ExpressionVisitor
{
public ParameterExpression NewParameter { get; set; }
protected override Expression VisitParameter(ParameterExpression node)
{
return this.NewParameter;
}
}
您可以按照 Titian 的回答所述手动执行此操作,但更简单的方法是使用 LinqKit。首先安装LinqKit nuget包,添加using LinqKit;
,然后:
// change terminaionDate to Expression<Func<MyEntity, DateTime>>
static Expression<Func<MyEntity, bool>> GetFilterPredicate(Expression<Func<MyEntity, DateTime>> terminationDate) {
// note Invoke
Expression<Func<Error, bool>> filter = c => terminationDate.Invoke(c) <= DateTime.Now;
// note Expand
return filter.Expand();
}
您可以在上面提供的 link 中阅读有关 Invoke
和 Expand
的信息。
我想将谓词构建逻辑移动到基础 class 中,但收到错误消息“LINQ to Entities 不支持 LINQ 表达式节点类型 'Invoke'”。我希望能够连接或有条件地链接谓词中的表达式。
我希望能够传递调用者所独有的谓词部分,即使用的 属性 名称(GetFilterPredicate 将成为将对具有不同 属性 名称并保存相关值的类型进行操作的通用例程。
protected Expression<Func<MyEntity, bool>> GetFilterPredicate(
PagingParameters pagingParameters,
Func<MyEntity, DateTime?> terminationDate,
Func<MyEntity, string> entityID,
Func<MyEntity, string> name
)
{
Expression<Func<MyEntity, bool>> validFilter = x => true;
if (pagingParameters.Active == true)
{
validFilter = x => terminationDate(x) > DateTime.Now;
}
///more conditions added here
return validFilter;
}
protected List<MyEntity> Query(IQueryable<MyEntity> source)
{
var filters = GetFilterPredicate(
new PagingParameters() { Active = true }
, i => i.TerminationDate
, i => i.EntityID
, i => i.Name
);
return source.Where(filters).AsNoTracking().ToList<MyEntity>();
}
您无法使用委托构建新表达式 terminationDate
您需要将 terminationDate
更改为表达式并使用它手动构建新表达式。
protected static Expression<Func<MyEntity, bool>> GetFilterPredicate(
PagingParameters pagingParameters,
Expression<Func<MyEntity, DateTime?>> terminationDate,
Expression<Func<MyEntity, string>> entityID,
Expression<Func<MyEntity, string>> name
)
{
Expression<Func<MyEntity, bool>> validFilter = x => true;
// We need to replace the parameter for all expressions with
// a common single parameter. I used the parameter for the default
// filter but a new Parameter expression would have worked as well.
// If you don't do this you will get an error (unbound parameter or something like that ) because the parameter
// used in the expressions (terminationDate, entityID) will be
// different then the parameter used for the new validFilter expression
var parameterReplacer = new ReplaceVisitor
{
NewParameter = validFilter.Parameters.First()
};
if (pagingParameters.Active == true)
{
validFilter = Expression.Lambda<Func<MyEntity, bool>>(
Expression.GreaterThan
(
parameterReplacer.Visit(terminationDate.Body),
Expression.Convert(Expression.Property(null, typeof(DateTime), "Now"), typeof(DateTime?))
),
parameterReplacer.NewParameter
);
}
// existing filter && x.EntityId != "A"
validFilter = Expression.Lambda<Func<MyEntity, bool>>(
Expression.And(
validFilter.Body,
Expression.NotEqual
(
parameterReplacer.Visit(entityID.Body),
Expression.Constant("A")
)
),
parameterReplacer.NewParameter
);
return validFilter;
}
/// <summary>
/// Simple Parameter Replacer, will replace the any parameter with the new
/// parameter. You will need to refine this if your expressions have nested
/// lambda, in that you will need to only replace the top most lambda
/// parameter, but for simple expressions it will work fine.
/// </summary>
class ReplaceVisitor : ExpressionVisitor
{
public ParameterExpression NewParameter { get; set; }
protected override Expression VisitParameter(ParameterExpression node)
{
return this.NewParameter;
}
}
您可以按照 Titian 的回答所述手动执行此操作,但更简单的方法是使用 LinqKit。首先安装LinqKit nuget包,添加using LinqKit;
,然后:
// change terminaionDate to Expression<Func<MyEntity, DateTime>>
static Expression<Func<MyEntity, bool>> GetFilterPredicate(Expression<Func<MyEntity, DateTime>> terminationDate) {
// note Invoke
Expression<Func<Error, bool>> filter = c => terminationDate.Invoke(c) <= DateTime.Now;
// note Expand
return filter.Expand();
}
您可以在上面提供的 link 中阅读有关 Invoke
和 Expand
的信息。