LINQ-to-SQL中针对Nullable<T>的IN/Contains()方法

12
我想在LINQ中生成这个SQL语句:
select * from Foo where Value in ( 1, 2, 3 )

棘手的部分似乎在于Value是允许空值的一个列。

相应的LINQ代码看起来应该是:

IEnumerable<Foo> foos = MyDataContext.Foos;
IEnumerable<int> values = GetMyValues();
var myFoos = from foo in foos
             where values.Contains(foo.Value)
             select foo;

当然,这段代码无法编译,因为foo.Value是一个可空的int?类型,而values则被声明为int类型。

我尝试过以下代码:

IEnumerable<Foo> foos = MyDataContext.Foos;
IEnumerable<int> values = GetMyValues();
IEnumerable<int?> nullables = values.Select( value => new Nullable<int>(value));
var myFoos = from foo in foos
             where nullables.Contains(foo.Value)
             select foo;

...还有这个:

IEnumerable<Foo> foos = MyDataContext.Foos;
IEnumerable<int> values = GetMyValues();
var myFoos = from foo in foos
             where values.Contains(foo.Value.Value)
             select foo;

这两个版本都给了我期望的结果,但它们没有生成我想要的SQL。看起来它们正在生成全表结果,然后在内存中执行Contains()过滤(即在普通的LINQ中,没有转换为SQL);在DataContext日志中没有IN子句。

有没有办法为可空类型生成SQL IN语句?

注意

事实证明,我遇到的问题与Contains或Nullable无关,因此我的问题措辞在很大程度上是不相关的。请查看@Nick Craver的接受答案以获取详细信息。

1个回答

21

这应该适用于你的例子:

IEnumerable<int> values = GetMyValues();
var myFoos = from foo in MyDataContext.Foos;
             where values.Contains(foo.Value.Value)
             select foo;

从一开始将类型转换为IEnumerable<T>将意味着执行将在SQL之外进行,而应直接调用IQueryable<Foo>。如果你将其转换为IEnumerable并在查询中使用它,则会获取所有的MyDataContext.Foos,然后使用该迭代器在C#中执行剩余的查询而不是在SQL中执行。

如果想要在SQL中运行,请不要在任何地方将其转换为IEnumerable。效果与在查询中使用MyDataContext.Foos.AsEnumerable()相同。


2
哇,我没想到将引用强制转换类型会有所区别(毫无疑问是由于使用了特定类型的扩展方法)。从面向对象编程的角度来看,这是不幸的。但是,它确实解决了我的问题。谢谢! - Craig Walker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接