Я реализовал базовый (наивный?) Поставщик LINQ, который работает нормально для моих целей, но есть ряд причуд, которые я хотел бы задать, но я не уверен, как это сделать. Например:Как сделать проекции LINQ-to-Objects?
// performing projection with Linq-to-Objects, since Linq-to-Sage won't handle this:
var vendorCodes = context.Vendors.ToList().Select(e => e.Key);
IQueryProvider
Моя реализация имела CreateQuery<TResult>
осуществление вида:
public IQueryable<TResult> CreateQuery<TResult>(Expression expression)
{
return (IQueryable<TResult>)Activator
.CreateInstance(typeof(ViewSet<>)
.MakeGenericType(elementType), _view, this, expression, _context);
}
Очевидно, что это душит, когда Expression
является MethodCallExpression
и TResult
является string
, поэтому я решил выполнить darn вещь:
public IQueryable<TResult> CreateQuery<TResult>(Expression expression)
{
var elementType = TypeSystem.GetElementType(expression.Type);
if (elementType == typeof(EntityBase))
{
Debug.Assert(elementType == typeof(TResult));
return (IQueryable<TResult>)Activator.CreateInstance(typeof(ViewSet<>).MakeGenericType(elementType), _view, this, expression, _context);
}
var methodCallExpression = expression as MethodCallExpression;
if(methodCallExpression != null && methodCallExpression.Method.Name == "Select")
{
return (IQueryable<TResult>)Execute(methodCallExpression);
}
throw new NotSupportedException(string.Format("Expression '{0}' is not supported by this provider.", expression));
}
Итак, когда я запускаю , я заканчиваю свою перегрузку private static object Execute<T>(Expression,ViewSet<T>)
, которая включает имя метода самого внутреннего выражения фильтра и делает фактические вызовы в базовом API.
Теперь, в этом случае я передаю выражение вызова в Select
метод, поэтому выражение фильтра null
и мой switch
блок получает пропущено - это хорошо, - где я застрял на здесь:
var method = expression as MethodCallExpression;
if (method != null && method.Method.Name == "Select")
{
// handle projections
var returnType = method.Type.GenericTypeArguments[0];
var expType = typeof (Func<,>).MakeGenericType(typeof (T), returnType);
var body = method.Arguments[1] as Expression<Func<T,object>>;
if (body != null)
{
// body is null here because it should be as Expression<Func<T,expType>>
var compiled = body.Compile();
return viewSet.Select(string.Empty).AsEnumerable().Select(compiled);
}
}
Что мне нужно сделать для моего MethodCallExpression
, чтобы передать его методу Select
LINQ-to-Objects? Правильно ли я это делаю?
Вы можете получить Expression из входящего метода выбора, а затем передать его в ViewSet Select или скомпилировать его как Func и передать его там. Таким образом, вы просто перепрограммируете проекцию на ваш viewSet. –
@SergeyLitvinov ok ... так как мне получить выражение 'Func>' из этого 'MethodCallExpression'? –
Также, если у меня нет дескриптора типа возвращаемого 'Func', как я могу применить его к правильному' Expression', чтобы иметь возможность доступа к методу 'Compile' и хранить вещи строго типизированными так что '.Select (convertExpression)' может работать? –