Reputation: 8098
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
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