我正在寻找一种合并多个表达式树的方法,以便为Entity Framework查询构建选择器。查询根据用户提供的参数知道要选择的列。例如,基本查询返回实体的ID / Name列。如果显式设置参数也检索Description列,则查询将返回ID / Name / Description。
所以,我需要以下代码中 MergeExpressions 方法的代码。
Expression<Func<T, TDto>> selector1 = x => new TDto
{
Id = x.Id,
Name = x.Name
}
Expression<Func<T, TDto>> selector2 = x => new TDto
{
Description = x.Description
}
var selector = selector1;
if (includeDescription)
selector = MergeExpressions(selector1, selector2);
var results = repo.All().Select(selector).ToList();
谢谢。
答案 0 :(得分:1)
对于一般情况不确定,但在样本中合并MemberInitExpression
曲率的lambdas相对容易。您只需要创建另一个MemberInitExpression
合并Bindings
:
static Expression<Func<TInput, TOutput>> MergeExpressions<TInput, TOutput>(Expression<Func<TInput, TOutput>> first, Expression<Func<TInput, TOutput>> second)
{
Debug.Assert(first != null && first.Body.NodeType == ExpressionType.MemberInit);
Debug.Assert(second != null && second.Body.NodeType == ExpressionType.MemberInit);
var firstBody = (MemberInitExpression)first.Body;
var secondBody = (MemberInitExpression)second.Body.ReplaceParameter(second.Parameters[0], first.Parameters[0]);
var body = firstBody.Update(firstBody.NewExpression, firstBody.Bindings.Union(secondBody.Bindings));
return first.Update(body, first.Parameters);
}
请注意,lambda表达式必须绑定到一个相同的参数,因此上面的代码使用以下参数replacer helper将第二个lambda body重新绑定到第一个lambda参数:
public static partial 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);
}
}
}
答案 1 :(得分:0)
示例:
Yes
或
Expression<Func<Customer, bool>> expr1 = (Customer c) => c.CompanyName.StartsWith("A");
Expression<Func<Customer, bool>> expr2 = (Customer c) => c.CompanyName.Contains("B");
var expr3 = PredicateBuilder.And(expr1, expr2);
var query = context.Customers.Where(expr3);
答案 2 :(得分:0)
我用扩展方法做这件事。它在语法上比在各处使用表达式树好一点。我称之为composable repositories。
我还编写了一个工具(LinqExpander)来组合不同扩展方法togeather的表达式树,这对于从数据库进行投影(选择)特别有用。当你使用子实体做事时,这只是nessacary。 (见我的帖子:Composable Repositories - Nesting extensions)
用法将是:
var dtos = context.Table
.ThingsIWant() //filter the set
.ToDtos() //project from database model to something else (your Selector)
.ToArray();//enumerate the set
ToDtos可能看起来像:
public static IQueryable<DtoType> ToDtos(this IQueryable<DatabaseType> things)
{
return things.Select(x=> new DtoType{ Thing = x.Thing ... });
}
你想合并两个选择togeather(我假设避免使用underfetch,但这看起来有点奇怪)。我会通过使用这样的投影来做到这一点:
context.Table
.AsExpandable()
.Select(x=>new {
Dto1 = x.ToDto1(),
Dto2 = x.ToDto2()
})
.ToArray();
如果您真的希望它返回这样的单个实体,您可能会执行以下操作:
context.Table
.AsExpandable()
.Select(x=> ToDto1(x).ToDto2(x));
但我从未尝试过这个。
由于这会使用子投影,因此您需要.AsExpandable扩展名。