これは役立つはずです。ただし、これを機能させるには、匿名タイプを具体化する必要があります。Expression<Func<Anonymous, Person>>
私のLinqPropertyChainは、匿名のまま作成するのが難しいため、それでは機能しません。
Expression<Func<Person, object>> orderBy = x => x.Name;
using(var dbContext = new MyDbContext())
{
var keyword = "term";
var startsWithResults = dbContext.People
.Where(x => x.Name.StartsWith(keyword))
.Select(x => new {
Rank = 1,
Entity = x,
});
var containsResults = dbContext.People
.Where(x => !startsWithResults.Select(y => y.Entity.Id).Contains(x.Id))
.Where(x => x.Name.Contains(keyword))
.Select(x => new {
Rank = 2,
Entity = x,
});
var rankedResults = startsWithResults.Concat(containsResults)
.OrderBy(x => x.Rank)
.ThenBy(LinqPropertyChain.Chain(x => x.Entity, orderBy));
// TODO: apply thenby ordering here based on the orderBy expression above
}
public static class LinqPropertyChain
{
public static Expression<Func<TInput, TOutput>> Chain<TInput, TOutput, TIntermediate>(
Expression<Func<TInput, TIntermediate>> outter,
Expression<Func<TIntermediate, TOutput>> inner
)
{
Console.WriteLine(inner);
Console.WriteLine(outter);
var visitor = new Visitor(new Dictionary<ParameterExpression, Expression>
{
{inner.Parameters[0], outter.Body}
});
var newBody = visitor.Visit(inner.Body);
Console.WriteLine(newBody);
return Expression.Lambda<Func<TInput, TOutput>>(newBody, outter.Parameters);
}
private class Visitor : ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, Expression> _replacement;
public Visitor(Dictionary<ParameterExpression, Expression> replacement)
{
_replacement = replacement;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (_replacement.ContainsKey(node))
return _replacement[node];
else
{
return node;
}
}
}
}
より少ない明示的なジェネリックでこれを行う方法を考え出しました。
Expression<Func<Person, object>> orderBy = x => x.Name;
Expression<Func<Foo, Person>> personExpression = x => x.Person;
var helper = new ExpressionChain(personExpression);
var chained = helper.Chain(orderBy).Expression;
// Define other methods and classes here
public class ExpressionChain<TInput, TOutput>
{
private readonly Expression<Func<TInput, TOutput>> _expression;
public ExpressionChain(Expression<Func<TInput, TOutput>> expression)
{
_expression = expression;
}
public Expression<Func<TInput, TOutput>> Expression { get { return _expression; } }
public ExpressionChain<TInput, TChained> Chain<TChained>
(Expression<Func<TOutput, TChained>> chainedExpression)
{
var visitor = new Visitor(new Dictionary<ParameterExpression, Expression>
{
{_expression.Parameters[0], chainedExpression.Body}
});
var lambda = Expression.Lambda<Func<TInput, TOutput>>(newBody, outter.Parameters);
return new ExpressionChain(lambda);
}
private class Visitor : ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, Expression> _replacement;
public Visitor(Dictionary<ParameterExpression, Expression> replacement)
{
_replacement = replacement;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (_replacement.ContainsKey(node))
return _replacement[node];
else
{
return node;
}
}
}
}