简体   繁体   English

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

[英]How can I convert IQueryable expression object into LINQ expression?

I need to be able to create LINQ's Where conditions/filters dynamically from a controller. 我需要能够创建LINQ的Where条件/动态地从一个控制器过滤器。 Then, I want to pass these filters to a repository's method that will query the database after applying the dynamic filters using LINQ's Where extension. 然后,我想将这些过滤器传递给存储库的方法,该方法将在使用LINQ的Where扩展应用动态过滤器后查询数据库。

Here is the code that I execute in my controller which dynamically creates filters using the IQueryable object 这是我在控制器中执行的代码,该代码使用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);

... 
...

Finally, in my repository I have this method that takes Expression<Func<StageModel, bool>> expression in the third argument and passes it to the Where extension if it isn't null. 最后,在我的存储库中,我有一个方法,该方法在第三个参数中使用Expression<Func<StageModel, bool>>表达式,如果不为null,则将其传递给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);

}

But I am getting an error on the following line 但是我在下一行出现错误

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

This is what the error shows 这是错误所显示的

Error 3 Argument 3: cannot convert from 'System.Linq.Expressions.MethodCallExpression' to 'System.Linq.Expressions.Expression>' 错误3参数3:无法从“ System.Linq.Expressions.MethodCallExpression”转换为“ System.Linq.Expressions.Expression>”

I also tried not casing the expression like so unitOfWork.Stages.GetStagesPagedList(1, PerPage, stage.Expression) 我也试过不使用像unitOfWork.Stages.GetStagesPagedList(1,PerPage,stage.Expression)这样的表达式

Error 3 Argument 3: cannot convert from 'System.Linq.Expressions.Expression' to 'System.Linq.Expressions.Expression>' 错误3参数3:无法从'System.Linq.Expressions.Expression'转换为'System.Linq.Expressions.Expression>'

How can I correctly do the conversion? 如何正确进行转换? Is this isn't possible, how can I dynamically create filters and pass them to my repository? 这是不可能的,如何动态创建过滤器并将其传递到我的存储库?

Using fake IQueryable to build predicate is not a good idea. 使用伪造的IQueryable构建谓词不是一个好主意。 The chained Where technique is applicable when you have the actual IQueryable . 当您拥有实际的IQueryable时,适用链式Where技术。 In order to build predicate expression, all you need is some predicate builder helper utility. 为了构建谓词表达式,您需要的是一些谓词生成器帮助器实用程序。

For instance, you can take my own PredicateUtils class from Establish a link between two lists in linq to entities where clause . 例如,您可以从建立linq中两个列表之间的链接到实体where子句中获取我自己的PredicateUtils类。 It perfectly fits because handles null predicates. 它非常适合,因为处理null谓词。

Copy/paste the class to your project, then use something like this (basically replace stage = stage.Where with predicate = predicate.And ): 将类复制/粘贴到您的项目中,然后使用类似的东西(基本上替换stage = stage.Where with 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);

... 
...

Simply have GetStagesPagedList accept the IQueryable that you have, rather than an Expression . 只需让GetStagesPagedList接受您拥有的IQueryable ,而不是Expression

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

I don't know that that's doing enough to really warrant another method, but you're free to use it if you want. 我不知道这样做是否足以保证使用另一种方法,但是您可以随意使用它。

Also your caller has a major bug in that you initialize the IQueryable to null , when you need to initialize it to the data context's table: 同样,您的调用方还有一个主要的错误,那就是当您需要将IQueryable初始化为null时,需要将其初始化为数据上下文的表:

IQueryable<StageModel> stages = CastedContext.Stages;

//...

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

You need to manually create an Expression like this: 您需要手动创建一个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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM