如何将IQueryable表达式对象转换为LINQ表达式?

时间:2017-01-20 20:21:34

标签: c# asp.net-mvc entity-framework linq expression

我需要能够从控制器动态创建LINQ的Where条件/过滤器。然后,我想将这些过滤器传递给存储库的方法,该方法将在使用LINQ的Where扩展名应用动态过滤器后查询数据库。

以下是我在控制器中执行的代码,它使用IQueryable对象

动态创建过滤器
IQueryable<StageModel> stage = null;

if(model.ClientId != null)
{
    stage = stage.Where(s => s.ClientId == model.ClientId);
}

if (model.CategoryIds != null && model.CategoryIds.Any())
{
    var stageIds = new List<int>{ 1, 2, 3 }; // this will be dynamically generated

    stage = stage.Where(s => stageIds.Contains(s.Id));
}

Stages = unitOfWork.Stages.GetStagesPagedList(1, PerPage, stage.Expression as MethodCallExpression);

... 
...

最后,在我的存储库中,我有这个方法,在第三个参数中使用Expression<Func<StageModel, bool>>表达式,如果它不为空,则将其传递给Where扩展名。

public IPagedList<StageModel> GetStagesPagedList(int pageNumber, int pageSize, Expression<Func<StageModel, bool>> predicate = null)
{
    IQueryable<StageModel> stages = CastedContext.Stages;

    if (predicate != null)
     {
         stages = stages.Where(predicate);
     }

     return stages.OrderBy(stage => stage.Name)
                  .ToPagedList(pageNumber, pageSize);

}

但是我在下一行收到错误

unitOfWork.Stages.GetStagesPagedList(1, PerPage, stage.Expression as MethodCallExpression)

这是错误显示的内容

  

错误3参数3:无法转换为&#39; System.Linq.Expressions.MethodCallExpression&#39;到&#39; System.Linq.Expressions.Expression&gt;&#39;

我也试过不像这样包装表达式 unitOfWork.Stages.GetStagesPagedList(1,PerPage,stage.Expression)

  

错误3参数3:无法转换   &#39; System.Linq.Expressions.Expression&#39;至   &#39;&System.Linq.Expressions.Expression GT;&#39;

如何正确进行转换?这是不可能的,我如何动态创建过滤器并将它们传递到我的存储库?

3 个答案:

答案 0 :(得分:1)

使用假IQueryable构建谓词不是一个好主意。当您拥有实际的Where时,链式IQueryable技术适用。为了构建谓词表达式,您只需要一些谓词构建器帮助程序实用程序。

例如,您可以从Establish a link between two lists in linq to entities where clause获取我自己的PredicateUtils课程。它非常适合因为句柄null谓词。

将类复制/粘贴到项目中,然后使用类似的内容(基本上将stage = stage.Where替换为predicate = predicate.And):

var predicate = PredicateUtils.Null<StageModel>();

if(model.ClientId != null)
{
    predicate = predicate.And(s => s.ClientId == model.ClientId);
}

if (model.CategoryIds != null && model.CategoryIds.Any())
{
    var stageIds = new List<int>{ 1, 2, 3 }; // this will be dynamically generated

    predicate = predicate.And(s => stageIds.Contains(s.Id));
}

Stages = unitOfWork.Stages.GetStagesPagedList(1, PerPage, predicate);

... 
...

答案 1 :(得分:0)

只需让GetStagesPagedList接受您拥有的IQueryable,而不是Expression

public IPagedList<StageModel> GetStagesPagedList(IQueryable<StageModel> stages, 
    int pageNumber, int pageSize)
{
     return stages.OrderBy(stage => stage.Name)
         .ToPagedList(pageNumber, pageSize);
}

我不知道那是否足以真正保证另一种方法,但如果你愿意,你可以自由使用它。

当您需要将IQueryable初始化为null时,您的调用者还有一个主要错误:

IQueryable<StageModel> stages = CastedContext.Stages;

//...

Stages = unitOfWork.Stages.GetStagesPagedList(stages, 1, PerPage);

答案 2 :(得分:0)

您需要手动创建Expression,如下所示:

var parameter = Expression.Parameter(typeof(StagedModel), "s");
Expression stage = null;
if (model.ClientId != null)
{
    stage = Expression.Equal(Expression.PropertyOrField(parameter, "ClientId"), Expression.Constant(model.ClientId));
}
if (model.CategoryIds != null && model.CategoryIds.Any())
{
    var stageIds = new List<int> { 1, 2, 3 };    
    Expression contains = null;
    foreach (var id in stageIds)
    {
        var equals = Expression.Equal(Expression.Constant(id), Expression.PropertyOrField(parameter, "Id"));
        contains = contains == null ? equals : Expression.OrElse(contains, equals);
    }

    stage = stage == null ? stage : Expression.AndAlso(stage, contains);
}    
var lambda = Expression.Lambda<Func<StagedModel, bool>>(stage, parameter);
Stages = unitOfWork.Stages.GetStagesPagedList(1, PerPage, stage);