简体   繁体   中英

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. 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.

Here is the code that I execute in my controller which dynamically creates filters using the IQueryable object

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.

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>'

I also tried not casing the expression like so unitOfWork.Stages.GetStagesPagedList(1, PerPage, stage.Expression)

Error 3 Argument 3: cannot convert from 'System.Linq.Expressions.Expression' to '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. The chained Where technique is applicable when you have the actual IQueryable . 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 . It perfectly fits because handles null predicates.

Copy/paste the class to your project, then use something like this (basically replace 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 .

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<StageModel> stages = CastedContext.Stages;

//...

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

You need to manually create an Expression like this:

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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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