无法将类型'System.Linq.IQueryable'隐式转换为'System.Data.Entity.DbSet' [英] Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Data.Entity.DbSet'

查看:41
本文介绍了无法将类型'System.Linq.IQueryable'隐式转换为'System.Data.Entity.DbSet'的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Linq的新手,所以下面有这些情况.

I'm new in Linq and so I have these situation below.

现在在编译过程中出现以下错误,Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Data.Entity.DbSet'.

Now below error during compilation, says 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,并且可以正常工作.

And so I tried to change var to IQueryable and it works.

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

但是,然后,我尝试再次对其进行更改(请参见下文),并且它可以正常工作.

But then, I tried to change it again (see below) and it works.

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

我只想知道另一个为什么起作用,而另一个却不起作用.

And I just want to know why the other one works, but the other's not.

对示例进行很好的解释可能会有所帮助.谢谢

A good explanation with example might help. Thanks

推荐答案

第一种情况不起作用的原因是System.Linq.IQueryable是由System.Data.Entity.DbSet类实现的接口.在C#中,如果类C实现接口I,则在类型之间进行转换时,您也可以将I视为C的基类(甚至语义class C : I都建议采用这种方法) .而且由于您不能隐式地(即非冗长地)将一个类(或接口)强制转换为其后代中的一个,因此在尝试执行此操作时会遇到编译时错误.您可以执行相反的操作,即将后代类隐式转换为其基类(或接口).这就是第二种情况.

The reason why the first scenario does not work is that the System.Linq.IQueryable is an interface which is implemented, among others, by the System.Data.Entity.DbSet class. In C#, if class C implements interface I, when it comes to transitions between types, you may as well treat I as C's base class (even the semantics class C : I suggest such an approach). And since you cannot implicitly (i.e. not verbosely) cast a class (or interface) to one of it's descendant classes, you get a compile-time error when trying to do it. You can do the opposite, that is implicitly cast a descendant class to it's base class (or interface). That's exactly what happens in the second scenario.

在您的情况下,可以通过显式强制转换来欺骗编译器:

In your case, you could trick the compiler by casting explicitly:

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

但我会强烈建议您不要这样做,因为您最终会遇到一个混乱的异常,因为query.Where(p => p.Id == id)的结果实际上不是DbSet<Customer>的实例,而是一些表示在DbSet<Customer>上执行的查询结果的类,该实现了IQueryable接口.

but I would strongly advise you not to since you'll end up with a messy exception, because the result of query.Where(p => p.Id == id) is not in fact an instance of DbSet<Customer>, but rather some class representing the result of a query performed on a DbSet<Customer>, which implements the IQueryable interface.

因此,总而言之,让我们看一下所有情况:

So, to sum up, let's go through all the scenarios:

方案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's 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); 
}

编辑

自从我回答这个问题已经有一段时间了,尽管它的优点仍然存在,但我还是倾向于使用略有不同的方法(在最初回答时可能还没有,我不确定).

It's been a while since I answered this question, and even though the merit of it still stands, I tend to use a slightly different approach (which might have not been available at the time of original answer, I'm not sure).

将实现IQueryable<T>的对象转换为IQueryable<T>的最简单(也是我认为最安全的)方法是:

The simplest (and I believe safest) way of casting an object implementing IQueryable<T> to IQueryable<T> is this:

var query = _db.Products.AsQueryable();

这只是将调用的主题返回到其IQueryable<T>接口实现.执行查询时,它不会产生任何开销.现在,有评论建议使用一些技巧,我认为使用这些技巧可能不是一个好主意.

This simply returns the subject of the call to its IQueryable<T> interface implementation. It should not produce any overhead when executing the query. Now, there are comments suggesting to use some tricks, using which I believe might be a bad idea.

此类技巧的一个示例是使用此方法:

One example of such trick is to use this:

var queryable = query.Select(x => x);

尽管查询对象时(几乎)完全是良性的,但在处理IQueryable<T>的某些实现时可能会造成一些危害.即,当查询被转换为例如SQL查询时,它很可能会将多余的"SELECT * FROM ..."添加到执行的查询中.那是最好的情况-在最可能的情况下,它会增加很多乏味的东西-如"SELECT x.P1, x.P2, ... FROM ... AS x".当然,您可能对此表示满意,但是您应该意识到这一点.意识到这样的事实,即根据执行情况,即使看起来什么也不做,这样的调用可能不是免费的".

While being (almost) completely benign when querying objects, it can do some harm when dealing with some implementations of IQueryable<T>. Namely, when the query is translated to, for example, a SQL query, it most likely will add a redundant "SELECT * FROM ..." to the executed query. That's the best case scenario - in the most probable scenario it will add something greatly more tedious - something like "SELECT x.P1, x.P2, ... FROM ... AS x". Of course, you might be OK with it, but you should be aware of it. Aware of the fact that, depending on implementation, calls like that might not be "free", even though appearing to do nothing.

另一个例子:

query.Where(x => true)

可能会在您的SQL查询中添加WHERE 1=1.

will potentially add a WHERE 1=1 to your SQL query.

这篇关于无法将类型'System.Linq.IQueryable'隐式转换为'System.Data.Entity.DbSet'的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆