Это сбой, потому что SelectMany<TSource, TResult>
метод ожидает
Expression<Func<TSource, IEnumerable<TResult>>>
во время передачи
Expression<Func<TSource, ICollection<TResult>>>
Это не то же самое, и последний не может быть преобразован в первый просто потому, что Expression<TDelegate>
- это класс , а классы инвариантны.
С учетом вашего кода ожидаемый лямбда-тип результата выглядит следующим образом:
var par = Expression.Parameter(origType, "x");
var propExpr = Expression.Property(par, property);
var firstGenType = reflectedType.GetGenericArguments()[0];
var resultType = typeof(IEnumerable<>).MakeGenericType(firstGenType);
Теперь вы можете использовать либоExpression.Convert
для изменения (приведения) типа свойства:
var lambda = Expression.Lambda(Expression.Convert(propExpr, resultType), par);
или (мое предпочтение) использовать другую перегрузку метода Expression.Lambda
с явным типом делегата (полученного через Expression.GetFuncType
):
var lambda = Expression.Lambda(Expression.GetFuncType(par.Type, resultType), propExpr, par);
Любая из них решит вашу исходную проблему.
Теперь, прежде чем вы получите следующее исключение, следующая строка:
var genericToListMethod = enumerableToListMethod.MakeGenericMethod(new[] { actualType });
также неверна (потому что, когда вы пропустите "Работа".Locations ", actualType
будет ICollection<Location>
, а не Location
, что ToList
ожидает), поэтому его необходимо изменить на:
var genericToListMethod = enumerableToListMethod.MakeGenericMethod(new[] { actual.ElementType });
В общем случае вы можете удалить actualType
переменная и всегда используйте IQueryable.ElementType
для этой цели.
Наконец, в качестве бонуса, не нужно искатьвручную общие определения методов.Expression.Call
имеет специальную перегрузку, которая позволяет легко «вызывать» статические обобщенные (и не только) методы по имени.Например, SelectMany
«вызов» будет выглядеть так:
selectExpression = Expression.Call(
typeof(Queryable), nameof(Queryable.SelectMany), new [] { origType, firstGenType },
queryable.Expression, lambda);
, а вызов Select
аналогичен.
Также нет необходимости создавать дополнительное лямбда-выражение, компилировать и динамически вызывать его для получения результирующего IQueryable
.То же самое может быть достигнуто с помощью метода IQueryProvider.CreateQuery
:
//var result = Expression.Lambda(selectExpression).Compile().DynamicInvoke() as IQueryable;
var result = queryable.Provider.CreateQuery(selectExpression);