Невозможно неявно преобразовать тип 'System.Linq.IQueryable' в 'System.Data.Entity.DbSet'

Я новичок в Linq, и поэтому у меня есть следующая ситуация.

Теперь, когда ошибка во время компиляции, говорит Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Data.Entity.DbSet'.

var query = _db.Products;
if (bool) {
  query = query.Where(p => p.Id == id);
}

И поэтому я попытался изменить var на IQueryable, и он работает.

IQueryable<Product> query = _db.Products;
if (bool) {
  query = query.Where(p => p.Id == id);
}

Но потом я попытался изменить его снова (см. ниже), и он работает.

var query = from product in products
            select product;
if (bool) {
  query = query.Where(p => p.Id == id);
}

И я просто хочу знать, почему другой работает, а другой нет.

Хорошее объяснение с примером может помочь. Благодаря

Ответы

Ответ 1

Причина, по которой первый сценарий не работает, заключается в том, что System.Linq.IQueryable - это интерфейс, который реализуется, среди прочих, классом System.Data.Entity.DbSet. В С#, если класс C реализует интерфейс I, когда дело доходит до переходов между типами, вы можете также рассматривать I как C базовый класс (даже семантика class C : I предлагает такой подход). И поскольку вы не можете неявно (т.е. Не дословно) использовать класс (или интерфейс) для одного из его классов-потомков, вы получаете ошибку времени компиляции при попытке сделать это. Вы можете сделать обратное, то есть неявно применить класс потомков к базовому классу (или интерфейсу). Именно это происходит во втором сценарии.

В вашем случае вы можете обмануть компилятор, произнеся явно:

query = (DbSet<Customer>) query.Where(p => p.Id == id);

но я бы сильно советовал вам не делать этого, так как вы закончите беспорядочное исключение, потому что результат query.Where(p => p.Id == id) на самом деле не является экземпляром DbSet<Customer>, а скорее класс, представляющий результат запроса, выполняемого на DbSet<Customer>, который реализует интерфейс IQueryable.

Итак, чтобы подвести итог, пропустите все сценарии:

Сценарий 1:

//query is of type DbSet<Customer>
var query = _db.Products; 
if (bool) {
  //here you're trying to assign a value of type IQueryable<Customer>
  //to a variable of it descendant type DbSet<Customer>
  //hence the compile-time error
  query = query.Where(p => p.Id == id); 
}

Сценарий 2:

//here you implicitly cast value of type DbSet<Customer>
//to IQueryable<Customer>, which is OK
IQueryable<Customer> query = _db.Products; 
if (bool) {
  //here you're assigning a value of type IQueryable<Customer>
  //to a variable of the same type, which is also OK
  query = query.Where(p => p.Id == id); 
}

Сценарий 3:

//I assume you have the following line in your code
var products = _db.Products;
//query is of type IQueryable<Customer>, because you perform
//a query on the DbSet<Product>
var query = from product in products
            select product;
if (bool) {
  //here you're assigning a value of type IQueryable<Customer>
  //to a variable of the same type, which is OK
  query = query.Where(p => p.Id == id); 
}

Ответ 2

При использовании var компилятор выводит тип выражения справа от назначения. Когда вы пишете

var query = _db.Products;

query имеет тип DbSet<Product>, и ему не может быть назначен IQueryable<Product>, который возвращает метод расширения Where.

Когда вы использовали синтаксис запроса, query снова был IQueryable<Product>, что заставило его работать. Это эквивалентно написанию

var query = products.Select(t => t);

Метод расширения Select, например Where, возвращает IQueryable<Product>.

Ответ 3

Это потому, что _db.Products не является запросом, это DbSet.

Второй блок работает, потому что вы отправляете его в IQueryable, а последний работает, потому что это фактический запрос.