¿Cuál es el propósito de AsQueryable ()?

107

¿El propósito es AsQueryable()solo para que pueda pasar un IEnumerablea métodos que podrían esperar IQueryable, o hay una razón útil para representar IEnumerablecomo IQueryable? Por ejemplo, se supone que debe ser para casos como este:

IEnumerable<Order> orders = orderRepo.GetAll();

// I don't want to create another method that works on IEnumerable,
// so I convert it here.
CountOrders(orders.AsQueryable());

public static int CountOrders(IQueryable<Order> ordersQuery)
{
    return ordersQuery.Count();
}

¿O realmente lo hace hacer algo diferente?

IEnumerable<Order> orders = orderRepo.GetAll();
IQueryable<Order> ordersQuery = orders.AsQueryable();

IEnumerable<Order> filteredOrders = orders.Where(o => o.CustomerId == 3);
IQueryable<Order> filteredOrdersQuery = ordersQuery.Where(o => o.CustomerId == 3);

// Are these executed in a different way?
int result1 = filteredOrders.Count();
int result2 = filteredOrdersQuery.Count();

¿Las IQueryableversiones de estos métodos de extensión simplemente crean una expresión que termina haciendo lo mismo una vez que se ejecuta? Mi pregunta principal es, ¿cuál es un caso de uso real para usar AsQueryable?

Ocelote20
fuente

Respuestas:

65

Hay algunos usos principales.

  1. Como se mencionó en otras respuestas, puede usarlo para simular una fuente de datos consultable utilizando una fuente de datos en memoria para que pueda probar más fácilmente métodos que eventualmente se usarán en una base no enumerable IQueryable.

  2. Puede escribir métodos auxiliares para manipular colecciones que se pueden aplicar a secuencias en memoria o fuentes de datos externas. Si escribe sus métodos de ayuda para usarlos por IQueryablecompleto, puede usarlos AsQueryableen todos los enumerables para usarlos. Esto le permite evitar escribir dos versiones separadas de métodos auxiliares muy generalizados.

  3. Le permite cambiar el tipo de tiempo de compilación de un queryable para que sea un IQueryabletipo más derivado. En efecto; lo usaría en un IQueryableal mismo tiempo que lo usaría AsEnumerableen un IEnumerable. Es posible que tenga un objeto que se implemente IQueryablepero que también tenga un Selectmétodo de instancia . Si ese fuera el caso y quisiera utilizar el Selectmétodo LINQ , necesitaría cambiar el tipo de tiempo de compilación del objeto a IQueryable. Podría simplemente lanzarlo, pero al tener un AsQueryablemétodo, puede aprovechar la inferencia de tipos. Esto es simplemente más conveniente si la lista de argumentos genéricos es compleja y, de hecho, es necesario si alguno de los argumentos genéricos es de tipo anónimo.

Servy
fuente
Gracias por el avivamiento. Marcando esto como la respuesta ya que es la más completa.
Ocelot20
5
Dado que se IQueryable<T>deriva de IEnumerable<T>, ¿puede explicar lo que quiere decir con "IQueryable no enumerable"?
Dai
2
@Dai Se refiere a IQueryableque es más que un paquete IEnumerable, y que en realidad aprovecha las capacidades adicionales de un IQueryable.
Servicio
# 1 es exactamente lo que estaba buscando. Gracias por verificar.
one.beat.consumer
12

El caso más válido que tengo para AsQueryable es la prueba unitaria. Digamos que tengo el siguiente ejemplo algo artificial

public interface IWidgetRepository
{
   IQueryable<Widget> Retrieve();
} 

public class WidgetController
{
   public IWidgetRepository WidgetRepository {get; set;}


   public IQueryable<Widget> Get()
   {
      return WidgetRepository.Retrieve();
   }
}

y quiero escribir una prueba unitaria para asegurarme de que el controlador devuelve los resultados devueltos desde el repositorio. Se vería algo como esto:

[TestMethod]
public void VerifyRepositoryOutputIsReturned()
{    
    var widget1 = new Widget();
    var widget2 = new Widget();
    var listOfWidgets = new List<Widget>() {widget1, widget2};
    var widgetRepository = new Mock<IWidgetRepository>();
    widgetRepository.Setup(r => r.Retrieve())
      .Returns(listOfWidgets.AsQueryable());
    var controller = new WidgetController();
    controller.WidgetRepository = widgetRepository.Object;

    var results = controller.Get();

    Assert.AreEqual(2, results.Count());
    Assert.IsTrue(results.Contains(widget1));
    Assert.IsTrue(results.Contains(widget2));
}

donde realmente, todo lo que el método AsQueryable () me permite hacer es satisfacer al compilador al configurar un simulacro.

Sin embargo, me interesaría dónde se usa esto en el código de la aplicación.

chris
fuente
11

Como señaló sanjuro, el propósito de AsQueryable () se explica en Uso de AsQueryable con Linq To Objects y Linq To SQL . En particular, el artículo dice:

Esto ofrece excelentes beneficios en escenarios de palabras reales donde tiene ciertos métodos en una entidad que devuelven un IQueryable de T y algunos métodos devuelven la Lista. Pero luego tiene un filtro de reglas comerciales que debe aplicarse en toda la colección, independientemente de si la colección se devuelve como IQueryable de T o IEnumerable de T. Desde el punto de vista del rendimiento, realmente desea aprovechar la ejecución del filtro comercial en la base de datos si la colección implementa IQueryable, de lo contrario, retroceda para aplicar el filtro comercial en la memoria usando Linq para objetar la implementación de los delegados.

Scott
fuente
6

El propósito de AsQueryable () se explica en gran medida en este artículo Uso de AsQueryable con Linq To Objects y Linq To SQL

De la sección Comentarios del método MSDN Queryable.AsQueryable:

Si el tipo de fuente implementa IQueryable, AsQueryable (IEnumerable) lo devuelve directamente. De lo contrario, devuelve un IQueryable que ejecuta consultas llamando a los métodos de operador de consulta equivalentes en Enumerable en lugar de los de Queryable.

Eso es exactamente lo que se menciona y se usa en el artículo anterior. En su ejemplo, depende de qué es orderRepo.GetAll regresando, IEnumerable o IQueryable (Linq to Sql). Si devuelve IQueryable, el método Count () se ejecutará en la base de datos; de lo contrario, se ejecutará en la memoria. Mire cuidadosamente el ejemplo en el artículo de referencia.

sanjuro
fuente
3

IQueryableDocumentación de cotización de interfaz :

La interfaz IQueryable está diseñada para que la implementen proveedores de consultas.

Entonces, para alguien que tiene la intención de hacer que su estructura de datos sea consultable en .NET, esa estructura de datos que no es necesaria puede ser enumerada o tener un enumerador válido .

IEnumeratores una interfaz para iterar y procesar el flujo de datos .

Tigran
fuente
¿Le importaría reformular esto: "esa estructura de datos que no es necesaria puede ser enumerada o tener un enumerador válido". Entiendo la diferencia entre IQueryabley IEnumerable, pero no entiendo el caso de uso del AsQueryablemétodo de extensión. Sin embargo, me estoy tropezando un poco con esa oración, que sospecho que podría tener la respuesta que estoy buscando (según los votos a favor).
Ocelot20
@ Ocelot20: eso significa que la estructura de datos presentada por el proveedor que implementa IQueryable puede no proporcionar una capacidad de enumeración. Depende del proveedor. Citando el documento: "Las consultas que no devuelven resultados enumerables se ejecutan cuando se llama al método Execute".
Tigran
Tal vez me esté perdiendo algo obvio, pero todavía no veo la respuesta a "¿por qué debería usar AsQueryable()?" Si estoy comenzando con un IEnumerable (algo que ya es capaz de proporcionar una capacidad de enumeración), ¿por qué tendría que convertir para IQueryableusar el método de extensión AsQueryable()? ¿Quizás un pequeño ejemplo de código para ilustrar dónde sería útil esto? Parece que me falta algo en tu explicación. Gracias por tu tiempo.
Ocelot20
@ Ocelot20: para mostrar realmente la diferencia, necesito escribir el proveedor de consultas. Usted sabe que tenemos linq to sql, linq to xmly entonces ... si desea escribir un linqcontrolador que se dirija a sus estructuras de datos ( linq to your data), para que los usuarios de su api tengan la posibilidad de ejecutar linqconsultas en sus estructuras de datos, debe elegirIQueryable
Tigran
2
Parece que estás explicando la diferencia entre IQueryabley IEnumerable. Sé la diferencia y no es eso lo que estoy pidiendo. Estoy preguntando por qué alguien querría tener algo que implementa IEnumerable, y convertirla en una IQueryableutilizando el AsQueryablemétodo de extensión. ¿Eso es lo que estás respondiendo? Todavía no puedo decir ...
Ocelot20