Digamos que necesito un método de extensión que seleccione solo las propiedades requeridas de diferentes fuentes. La fuente podría ser la base de datos o la colección en memoria. Así que he definido tal método de extensión:
public IQueryable<TResult> SelectDynamic<TResult>( this IQueryable<T> source, ...)
Esto funciona bien para IQueryable
s. Pero tengo que llamar a esta función también para IEnumerable
s.
Y en ese caso, puedo llamarlo con la ayuda de .AsQueryable()
:
myEnumerable.AsQueryable() .SelectDynamic(...) .ToList();
Ambos funcionan bien. Y si ambos funcionan bien, ¿ en qué condiciones tengo que crear dos métodos de extensión diferentes para el mismo propósito, uno para IEnumerable
y otro para IQueryable
?
Mi método tiene que enviar una consulta a la base de datos en el caso de Queryable
.
Por ejemplo, aquí está la fuente del método de extensión .Select
dentro del espacio de nombres System.Linq
:
Vuelvo a repetir mi pregunta principal:
Mi método debe enviar una consulta a la base de datos en el caso de Queryable
, pero no cuando se trabaja con IEnumerable
. Y por ahora, estoy usando AsQueryable()
para los enumerables. Porque no quiero escribir el mismo código para el Enumerable
. ¿Puede tener algunos efectos secundarios?
Si su código solo funciona cuando los objetos con los que trata están cargados en la memoria, simplemente proporcione la variante IEnumerable
y deje que las personas que llaman decidan cuándo quieren convertir un IEnumerable
en un IQueryable
en memoria.
En general, no implementará nuevas variaciones en torno a IQueryable
a menos que esté escribiendo un nuevo proveedor de base de datos.
myEnumerable.AsQueryable()
devuelve un objeto personalizado: new EnumerableQuery<TElement>(myEnumerable);
( código fuente )
Esta clase EnumerableQuery
implementa IEnumerable<T>
e IQueryable<T>
Cuando se usa el resultado de EnumerableQuery
de .AsQueryable()
como IEnumerable
, la implementación del método de interfaz IEnumerable<T>.GetIterator()
simplemente devuelve el iterador de origen original, por lo que no hay cambios y la sobrecarga es mínima.
Cuando se usa el resultado de .AsQueryable()
como IQueriable
, la implementación de la propiedad de interfaz IQueriable.Expression
simplemente devuelve Expression.Constant(this)
, lista para ser evaluada más tarde como IEnumerable cuando se consume todo el árbol de expresión.
(Todos los demás métodos y rutas de código de EnumerableQuery
no son realmente relevantes, cuando EnumerableQuery se construye directamente desde un IEnumerable, por lo que puedo decir)
Si lo entiendo correctamente, implementó su método selectDynamic<TResult>()
de tal manera que construye un árbol de expresión dentro del método, que produce el resultado deseado cuando se compila.
Según entiendo el código fuente, cuando llama, por ejemplo myEnumerable.AsEnumerable().selectDynamic().ToList()
, el árbol de expresiones que construyó se compila y ejecuta en myEnumerable, y la sobrecarga total debería ser bastante mínima, ya que todos este trabajo solo se realiza una vez por consulta, no una vez por elemento.
Entonces, creo que no hay nada de malo en implementar su método de extensión IEnumerable de esta manera:
public IEnumerable<TResult> SelectDynamic<TResult>( this IEnumerable<T> source,...) return source.AsQueryable().SelectDynamic(); }
Hay una pequeña sobrecarga, ya que compila la consulta una vez cada vez que se llama a este método, y no estoy seguro de que JITer sea lo suficientemente inteligente como para almacenar en caché esta compilación. Pero creo que eso no se notará en la mayoría de las circunstancias, a menos que ejecute esta consulta mil veces por segundo.
No debería haber otros efectos secundarios, además de problemas leves de rendimiento, al implementar el método de extensión IEnumerable de esta manera.