Я создаю генератор запросов на основе LINQ.
Одной из возможностей является возможность указать произвольную проекцию на стороне сервера как часть определения запроса.Например:
class CustomerSearch : SearchDefinition<Customer>
{
protected override Expression<Func<Customer, object>> GetProjection()
{
return x => new
{
Name = x.Name,
Agent = x.Agent.Code
Sales = x.Orders.Sum(o => o.Amount)
};
}
}
Поскольку пользователь должен иметь возможность сортировать свойства проекции (в отличие от свойств клиента), я воссоздаю выражение как Func<Customer,anonymous type>
вместо Func<Customer, object>
:
//This is a method on SearchDefinition
IQueryable Transform(IQueryable source)
{
var projection = GetProjection();
var properProjection = Expression.Lambda(projection.Body,
projection.Parameters.Single());
Чтобы вернуть спроецированный запрос, я хотел бы иметь возможность сделать это (что, фактически, работает практически в качестве доказательства концепции):
return Queryable.Select((IQueryable<TRoot>)source, (dynamic)properProjection);
TRoot - это параметр типа в SearchDefinition.Это приводит к следующему исключению:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:
The best overloaded method match for
'System.Linq.Queryable.Select<Customer,object>(System.Linq.IQueryable<Customer>,
System.Linq.Expressions.Expression<System.Func<Customer,object>>)'
has some invalid arguments
at CallSite.Target(Closure , CallSite , Type , IQueryable`1 , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecute3[T0,T1,T2,TRet]
(CallSite site, T0 arg0, T1 arg1, T2 arg2)
at SearchDefinition`1.Transform(IQueryable source) in ...
Если вы посмотрите внимательно, это неверно выводит общие параметры: Customer,object
вместо Customer,anonymous type
, что является фактическим типом выражения properProjection
(double-checked)
Мой обходной путь использует отражение.Но с общими аргументами, это настоящий беспорядок:
var genericSelectMethod = typeof(Queryable).GetMethods().Single(
x => x.Name == "Select" &&
x.GetParameters()[1].ParameterType.GetGenericArguments()[0]
.GetGenericArguments().Length == 2);
var selectMethod = genericSelectMethod.MakeGenericMethod(source.ElementType,
projectionBody.Type);
return (IQueryable)selectMethod.Invoke(null, new object[]{ source, projection });
Кто-нибудь знает лучший способ?
Обновление :Причина сбоя dynamic
в том, что анонимные типы определены как internal
.Вот почему это сработало с использованием проекта проверки концепции, в котором все было в одной сборке.
Я в этом крута.Я все еще хотел бы найти более чистый способ найти правильную Queryable.Select
перегрузку.