Not easy; you need to rewrite all the expressions - well, strictly speaking, you can rework most of them, but the problem is that you have different x
in each (although it looks the same), so you need to use the visitor to replace all the parameters with the final x
. Fortunately, this is not so bad in 4.0:
static void Main() { 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() };
This uses the constructor from the first expression found, so you might need sanity - make sure everyone else uses the trivial constructors in their respective NewExpression
s. However, I left it to the reader.
Edit: in the comments, @Slaks notes that more LINQ can make it shorter. He's right, of course - a little tight for easy reading, though:
static Expression<Func<TSource, TDestination>> Combine<TSource, TDestination>( params Expression<Func<TSource, TDestination>>[] selectors) { var param = Expression.Parameter(typeof(TSource), "x"); return Expression.Lambda<Func<TSource, TDestination>>( Expression.MemberInit( Expression.New(typeof(TDestination).GetConstructor(Type.EmptyTypes)), from selector in selectors let replace = new ParameterReplaceVisitor( selector.Parameters[0], param) from binding in ((MemberInitExpression)selector.Body).Bindings .OfType<MemberAssignment>() select Expression.Bind(binding.Member, replace.VisitAndConvert(binding.Expression, "Combine"))) , param); }
Marc gravell
source share