How to combine several similar SELECT-expressions into a single expression?
private static Expression<Func<Agency, AgencyDTO>> CombineSelectors(params Expression<Func<Agency, AgencyDTO>>[] selectors)
{
// ???
return null;
}
private void Query()
{
Expression<Func<Agency, AgencyDTO>> selector1 = x => new AgencyDTO { Name = x.Name };
Expression<Func<Agency, AgencyDTO>> selector2 = x => new AgencyDTO { Phone = x.PhoneNumber };
Expression<Func<Agency, AgencyDTO>> selector3 = x => new AgencyDTO { Location = x.Locality.Name };
Expression<Func<Agency, AgencyDTO>> selector4 = x => new AgencyDTO { EmployeeCount = x.Employees.Count() };
using (RealtyContext context = Session.CreateContext())
{
IQueryable<AgencyDTO> agencies = context.Agencies.Select(CombineSelectors(selector3, selector4));
foreach (AgencyDTO agencyDTO in agencies)
{
// do something..;
}
}
}
If all of the selectors will only initialize
AgencyDTO
objects (like your example), you can cast the expressions toNewExpression
instances, then callExpression.New
with theMembers
of the expressions.You'll also need an
ExpressionVisitor
to replace theParameterExpression
s from the original expressions with a singleParameterExpression
for the expression you're creating.In case anyone else stumbles upon this with a similar use case as mine (my selects targeted different classes based on the level of detail needed):
Simplified scenario:
I adapted the solution provided by @Marc Gravell like so:
The
Map
method of the extended class then becomes:Not simple; you need to rewrite all the expressions - well, strictly speaking you can recycle most of one of them, but the problem is that you have different
x
in each (even though it looks the same), hence you need to use a visitor to replace all the parameters with the finalx
. Fortunately this isn't too bad in 4.0:This uses the constructor from the first expression found, so you might want to sanity-check that all of the others use trivial constructors in their respective
NewExpression
s. I've left that for the reader, though.Edit: In the comments, @Slaks notes that more LINQ could make this shorter. He is of course right - a bit dense for easy reading, though: