Ho implementato un provider LINQ di base (ingenuo?) Che funziona correttamente per i miei scopi, ma c'è un numero di stranezze che mi piacerebbe affrontare, ma non sono sicuro di come. Per esempio:Come rendere LINQ-to-Objects gestire le proiezioni?
// performing projection with Linq-to-Objects, since Linq-to-Sage won't handle this:
var vendorCodes = context.Vendors.ToList().Select(e => e.Key);
mio IQueryProvider
implementazione ha avuto un CreateQuery<TResult>
implementazione simile a questo:
public IQueryable<TResult> CreateQuery<TResult>(Expression expression)
{
return (IQueryable<TResult>)Activator
.CreateInstance(typeof(ViewSet<>)
.MakeGenericType(elementType), _view, this, expression, _context);
}
Ovviamente questo soffoca quando il Expression
è un MethodCallExpression
e TResult
è un string
, così ho pensato di eseguire la cosa 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));
}
Così, quando corro , finisco nel mio overload private static object Execute<T>(Expression,ViewSet<T>)
, che attiva il nome del metodo dell'espressione del filtro più interno e effettua le chiamate effettive nell'API sottostante.
Ora, in questo caso sto passando l'espressione chiamata Select
metodo, quindi l'espressione di filtro è null
e il mio blocco switch
viene saltato - che va bene - dove mi sono bloccato in è qui:
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);
}
}
Cosa devo fare al mio MethodCallExpression
per poterlo passare al metodo Select
di LINQ-to-Objects? Mi sto avvicinando a questo correttamente?
È possibile ottenere Expression dal metodo Select in ingresso, quindi passarlo a ViewSet Select o compilarlo come Func e passarlo lì. Quindi dovrai semplicemente inviare nuovamente la proiezione al tuo viewSet. –
@SergeyLitvinov ok ... quindi come faccio ad avere un 'espressione> 'da quel' MethodCallExpression'? –
Anche se non ho un handle sul tipo di ritorno di 'Func', come posso lanciarlo sull'esatto' Expression 'in modo da poter accedere al metodo' Compile' e mantenere le cose fortemente tipizzate in modo che '.Select (convertedExpression)' possa funzionare? –