我有一个与this previous question of mine相关的问题。在现有的LINQ中涉及大量连接,我试图采用包含连接的每个单独方法并将其转换为 CompiledQuery 。
首先,正常的LINQ方法:
private IQueryable<Widget> GetWidgetQuery()
{
return db.Widgets.Where(u => (!u.SomeField.HasValue || !u.SomeField.Value));
}
在这里,CompiledQuery的委托(字段)定义沿着以下几行:
private static readonly Func<DBDataContext, IQueryable<Widget>> GetWidgetQuery =
CompiledQuery.Compile((DBDataContext db) =>
db.Widgets.Where(u => (!u.SomeField.HasValue || !u.SomeField.Value)));
如果我将鼠标悬停在方法 GetWidgetQuery()的正常LINQ语句上,我会发现它是方法,如下所示:
(method) IQueryable<Widget> GetWidgetQuery()
但是,已编译的查询委托(字段)的不同之处如下:
(field) Func<DBDataContext, IQueryable<Widget>> GetWidgetQuery
作为LINQ语句的一部分执行后者时,语法不同如下。首先,正常LINQ参与加入:
var myquery =
from wxr in GetWidgetXRQuery()
join w in GetWidgetQuery() on wxr.WidgetID equals w.ID
select new DTO.WidgetList
{
...
}
在这里,以委托形式调用CompiledQuery:
var myquery =
from wxr in GetWidgetXRQuery()
join w in GetWidgetQuery.Invoke(myContext) on wxr.WidgetID equals w.ID
select new DTO.WidgetList
{
...
}
前者返回预期的结果集;后者,当我尝试 myquery.ToList()时,产生一个 stackoverflow 异常,部分与this limitation of .NET 3.5相关,我想。
有人可以帮助我理解编译语句如何作为一个字段存在(或者我想我应该说一个委托)而不是一个方法正在杀死我的查询?总之,我知道我在做什么是错的,但我不确定我理解我误解了什么。
答案 0 :(得分:1)
我尝试做与你在EF 4上做的大致相同的事情,一切似乎都运转良好。因此,它可能是EF 3.5问题,也可能与您GetWidgetXRQuery
的实施或两者的某些组合有关。
但我要提出的真正观点是,在回答上一个问题时,Roy Goode stated,一旦以任何方式扩展该查询,就会失去预编译查询的所有优势。通过尝试对查询执行加入,您将其转换为纯粹的旧查询。所以你不妨使用看起来适合你的非编译版本。
意识到你在谈论LINQ to SQL。这种查询确实似乎在Entity Framework中有支持,但在LINQ to SQL中却没有。在.NET 4中,我收到以下错误:
不支持返回自引用常量表达式的IQueryable。
这对我来说意义不大,但我猜测它与内部表示编译查询的方式有关。如果我将查询评估为变量并稍后在查询中使用该变量,我仍会得到相同的错误,因此它显然与委托和函数之间的差异无关。我仍然认为编译的查询不适合在这里使用。您需要创建一个大的编译查询来表示您要执行的整个查询,或者如果您想以这种方式将它们拼凑在一起,则需要使用常规查询。
答案 1 :(得分:0)
我在进行数据库集成测试时遇到了同样的错误,并且直接跳到了这一点,而没有试图解释我的具体问题。当使用IQueryable时,Linq to Sql会在内部创建sql查询,而当您在该IQueryable上执行方法时,即ToList(),它将在数据库上执行该查询。因此,以我为例,我加入了一个返回IQueryable但被模拟以返回结果的方法,它试图将其编译为sql查询,但是我创建的IQueryable没有内部SQL查询