Dragos Durlut
Dragos Durlut

Reputation: 8098

Expression<Func<TSource, TResult>> selector with parameter

I am trying to create a LINQ expression selector to accomodate another IQueryable as a parameter. The dot.net fiddle is here

I am trying to code the CustomSelector to accept an IQueryable parameter and apply it to the selector depending on case.

Also, I want to include in the call of CustomSelector the value of each row to apply to the IQueryable parameter.

This way I will not have to write 2 Custom selectors.

The alternative is GetMyData2 function.

The test code is below:

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

public class Entity
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class EntityDto
{
    public int Id { get; set; }
    public string Name { get; set; }    
    public int Count { get; set; }
}

public class TestController 
{
    public List<Entity> entityList { get; set; }


    public List<EntityDto> GetMyData(string condition)
    {
        List<Entity> sourceEntityList = new List<Entity>();
        List<EntityDto> returnEntityList = new List<EntityDto>();

        switch(condition)
        {
            case "A":       
            returnEntityList = sourceEntityList.Where(x=>x.Name == "A").Select(y=>CustomSelector(sourceEntityList.Where(z=>z.Name=x.Name)));
        return returnEntityList;
            break;
        case "B":       
        default:
            returnEntityList = sourceEntityList.Where(x=>x.Name == "B").Select(y=>CustomSelector(sourceEntityList.Where(z=>z.Name != x.Name)));
        return returnEntityList;
            break;
        }

    }

    public List<EntityDto> GetMyData2(string condition)
    {
        List<Entity> sourceEntityList = new List<Entity>();
        List<EntityDto> returnEntityList = new List<EntityDto>();

        switch(condition)
        {
            case "A":       
            returnEntityList = sourceEntityList.Where(x=>x.Name == "A").Select(ent=>
                                                                               new EntityDto()
                {
                    Id = ent.Id
                    ,
                    Name = ent.Name                                       
                    ,
                    Count =  sourceEntityList.Count(z=>z.Name== ent.Name)
                }
                                                                              ).ToList();
        return returnEntityList;
            break;
        case "B":       
        default:
            returnEntityList = sourceEntityList.Where(x=>x.Name == "B").Select(ent=>
                                                                               new EntityDto()
                {
                    Id = ent.Id
                    ,
                    Name = ent.Name                                       
                    ,
                    Count =  sourceEntityList.Count(z=>z.Name != ent.Name)
                }
                                                                              ).ToList();
        return returnEntityList;
            break;
        }

    }

    protected Func<Entity, EntityDto> CustomSelector(IQueryable<Entity> paramQuery)
        {

                return ent => new EntityDto()
                {
                    Id = ent.Id
                    ,
                    Name = ent.Name                                       
                    ,
                    Count =  paramQuery.Count()
                };

        }
}

Upvotes: 0

Views: 1666

Answers (1)

Ivan Stoev
Ivan Stoev

Reputation: 205679

What are you asking is not possible w/o some expression tree manipulation.

You can use some 3rd party packages like LinqKit (AsExpandable / Invoke) or AutoMapper, but here is a custom solution based on expression prototype and simple parameter replacer.

Parameter replacer:

public static class ExpressionUtils
{
    public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
    {
        return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
    }

    class ParameterReplacer : ExpressionVisitor
    {
        public ParameterExpression Source;
        public Expression Target;
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return node == Source ? Target : base.VisitParameter(node);
        }
    }
}

CustomSelector:

protected Expression<Func<Entity, EntityDto>> CustomSelector(Expression<Func<Entity, IQueryable<Entity>>> paramQuery)
{
    Expression<Func<Entity, IQueryable<Entity>, EntityDto>> prototype = (e, q) => new EntityDto
    {
        Id = e.Id,
        Name = e.Name,
        Count = q.Count(),
    };
    return Expression.Lambda<Func<Entity, EntityDto>>(
        prototype.Body
            .ReplaceParameter(prototype.Parameters[0], paramQuery.Parameters[0])
            .ReplaceParameter(prototype.Parameters[1], paramQuery.Body),
        paramQuery.Parameters[0]);
}

Usage:

public List<EntityDto> GetMyData(string condition)
{
    var sourceEntityList = new List<Entity>().AsQueryable();
    switch (condition)
    {
        case "A":
            return sourceEntityList.Where(x => x.Name == "A")
                .Select(CustomSelector(x => sourceEntityList.Where(y => y.Name == x.Name))).ToList();
        case "B":
        default:
            return sourceEntityList.Where(x => x.Name == "B")
                .Select(CustomSelector(x => sourceEntityList.Where(y => y.Name != x.Name))).ToList();
    }
}

Upvotes: 2

Related Questions