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


¿Es el propósito de AsQueryable() solo para que pueda pasar un IEnumerable a los métodos que podrían esperar IQueryable, o hay una razón útil para representar IEnumerable como IQueryable? Por ejemplo, se supone que es 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 versiones IQueryable de estos métodos de extensión solo construyen 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?

Author: Soner Gönül, 2013-06-28

5 answers

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 los métodos que eventualmente se utilizarán en un IQueryable no enumerable.

  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 usar IQueryable por completo, puede usar AsQueryable en todos los enumerables para usarlos. Esto le permite evitar escribir dos versiones separadas de métodos de ayuda muy generalizados.

  3. Le permite cambiar el tipo de tiempo de compilación de un consultable a IQueryable, en lugar de algún tipo más derivado. En efecto, lo usarías en un IQueryable al mismo tiempo que usarías AsEnumerable en un IEnumerable. Es posible que tenga un objeto que implemente IQueryable pero que también tenga un método instance Select. Si ese fuera el caso, y quisieras usar el LINQ Select método, tendría que cambiar el tipo de tiempo de compilación del objeto a IQueryable. Podrías simplemente lanzarlo, pero al tener un método AsQueryable puedes tomar ventaja de la inferencia de tipos. Esto es simplemente más conveniente si la lista de argumentos genéricos es compleja, y en realidad es necesario si cualquiera de los argumentos genéricos son tipos anónimos.

 48
Author: Servy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-04 15:24:54

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 mock.

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

 10
Author: chris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-04 12:45:28

Como señaló sanjuro, el propósito de AsQueryable() se explica en Usando 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 una Lista. Pero entonces usted tiene filtro de regla de negocio que necesita ser aplicado en toda la colección sin importar si la colección se devuelve como IQueryable de T oEnumerable de T. Desde un punto de vista de rendimiento, realmente desea aprovechar la ejecución del filtro de negocio en la base de datos si la colección implementa IQueryable, de lo contrario, vuelva a aplicar el filtro de negocio en memoria utilizando Linq para la implementación de objetos de delegados.

 8
Author: Scott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-04 23:38:43

Interfaz IQueryable citando documentación:

La interfaz IQueryable está destinada a la implementación mediante consulta proveedor.

Así que para alguien que pretenda hacer su datastracture consultable en.NET, esa datastracture que no sea necesaria puede ser enumerada o tener un enumerador válido.

IEnumerator es una interfaz para iterar y procesar flujo de datos en su lugar.

 2
Author: Tigran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-28 14:27:20

El propósito de AsQueryable() se explica en gran medida en este artículo Usando AsQueryable Con Linq To Objects Y Linq To SQL

De la sección Observaciones de MSDN Queryable.Método de AsQueryable:

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

Eso es exactamente lo que se menciona y se utiliza en el artículo anterior. En su ejemplo, depende de lo que es orderRepo.getAll returning ,Enumerable o IQueryable (Linq a Sql). Si devuelve IQueryable, el método Count () se ejecutará en la base de datos, de lo contrario se ejecutará en memoria. Mire cuidadosamente el ejemplo en el artículo referenciado.

 2
Author: sanjuro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-04 15:13:49