La guía o código que encontrarás en este post es la solución más fácil y válida que encontramos a tus dudas o problema.
Solución:
La razón por la cual el primer escenario no funciona es que el System.Linq.IQueryable
es una interfaz que es implementada, entre otros, por el System.Data.Entity.DbSet
clase. En C#, si la clase C
interfaz de implementos I
cuando se trata de transiciones entre tipos, también puede tratar I
como C
la clase base de (incluso la semántica class C : I
sugerir tal enfoque). Y dado que no puede implícitamente (es decir, no detalladamente) convertir una clase (o interfaz) a una de sus clases descendientes, obtiene un error de tiempo de compilación cuando intenta hacerlo. Puede hacer lo contrario, es decir, convertir implícitamente una clase descendiente en su clase base (o interfaz). Eso es exactamente lo que sucede en el segundo escenario.
En su caso, podría engañar al compilador emitiendo explícitamente:
query = (DbSet) query.Where(p => p.Id == id);
pero yo podria fuertemente le aconsejo que no lo haga ya que terminará con una excepción desordenada, porque el resultado de query.Where(p => p.Id == id)
no es de hecho un ejemplo de DbSet
sino alguna clase que representa el resultado de una consulta realizada en un DbSet
que implementa la IQueryable
interfaz.
Entonces, para resumir, repasemos todos los escenarios:
Escenario 1:
//query is of type DbSet
var query = _db.Products;
if (bool)
//here you're trying to assign a value of type IQueryable
//to a variable of it's descendant type DbSet
//hence the compile-time error
query = query.Where(p => p.Id == id);
Escenario 2:
//here you implicitly cast value of type DbSet
//to IQueryable, which is OK
IQueryable query = _db.Products;
if (bool)
//here you're assigning a value of type IQueryable
//to a variable of the same type, which is also OK
query = query.Where(p => p.Id == id);
Escenario 3:
//I assume you have the following line in your code
var products = _db.Products;
//query is of type IQueryable, because you perform
//a query on the DbSet
var query = from product in products
select product;
if (bool)
//here you're assigning a value of type IQueryable
//to a variable of the same type, which is OK
query = query.Where(p => p.Id == id);
EDITAR
Ha pasado un tiempo desde que respondí esta pregunta, y aunque el mérito sigue en pie, tiendo a usar un enfoque ligeramente diferente (que podría no haber estado disponible en el momento de la respuesta original, no estoy seguro).
La forma más simple (y creo que la más segura) de lanzar un objeto implementando IQueryable
para IQueryable
Es esto:
var query = _db.Products.AsQueryable();
Esto simplemente devuelve el asunto de la llamada a su IQueryable
implementación de la interfaz. No debería producir ninguna sobrecarga al ejecutar la consulta. Ahora, hay comentarios que sugieren usar algunos trucos, que creo que podrían ser una mala idea.
Un ejemplo de tal truco es usar esto:
var queryable = query.Select(x => x);
Si bien es (casi) completamente benigno cuando se consultan objetos, puede causar algún daño cuando se trata de algunas implementaciones de IQueryable
. Es decir, cuando la consulta se traduce, por ejemplo, a una consulta SQL, lo más probable es que agregue una "SELECT * FROM ..."
a la consulta ejecutada. Ese es el mejor de los casos: en el escenario más probable, agregará algo mucho más tedioso, algo como "SELECT x.P1, x.P2, ... FROM ... AS x"
. Por supuesto, puede estar de acuerdo con eso, pero debe ser consciente de ello. Consciente del hecho de que, según la implementación, las llamadas como esa pueden no ser “gratuitas”, aunque parezca que no hacen nada.
Otro ejemplo:
query.Where(x => true)
potencialmente agregará un WHERE 1=1
a su consulta SQL.
Cuando usas var
el compilador infiere el tipo de la expresión a la derecha de la asignación. Cuando escribes
var query = _db.Products;
query
es de tipo DbSet
y no se le puede asignar ninguna IQueryable
que la Where
método de extensión devuelve.
Cuando usó la sintaxis de consulta, query
estaba de nuevo IQueryable
, lo que hizo que funcionara. es equivalente a escribir
var query = products.Select(t => t);
los Select
método de extensión, como Where
devoluciones IQueryable
.
Aquí puedes ver las reseñas y valoraciones de los lectores
Si estás de acuerdo, tienes la opción de dejar un enunciado acerca de qué te ha gustado de este ensayo.