LINQ 和 VB.NET 以及编译的表达式树正在破坏 SubSonic 3
我一直在尝试解决 VB.NET 及其生成的表达式树的一个小问题。
我有一个简单的测试...
Public Sub ActiveRecord_Find_By_NonKey_Returns_123()
Dim orders = Order.Find(Function(item As Order) item.EmployeeID = 1)
Assert.Equal(Of Integer)(123, orders.Count)
End Sub
人们希望它能工作,但 item.EmployeeID = 1
位有问题,而且这只是 VB.NET 的问题,而不是 C# 的问题。 VB.NET 喜欢巧妙地处理它编译的表达式树,因为 item.EmployeeID
可以为空! (阅读与此相关的博客)
问题在于表达式 item.EmployeeID = 1
被 Convert
表达式节点包裹。此时 TSql 生成器完全丢失并创建以下 WHERE
子句。
WHERE ([t0].[EmployeeID] = 1) <> 0
该代码片段在数据库上执行时往往会失败。
所以这似乎是 SubSonic 中的一个错误。不幸的是,试图弄清楚如何/在哪里修复它正在绞尽脑汁!
不过它确实变得更有趣了。
Public Sub ActiveRecord_Find_By_NonKey_Returns_123_Linq()
Dim orders = From item In Order.All Where item.EmployeeID = 1 Select item
Assert.Equal(Of Integer)(123, orders.Count)
End Sub
但它的 WHERE
子句是......
WHERE COALESCE(CASE WHEN (([t0].[EmployeeID] = 1)) THEN 1 ELSE 0 END, 0) <> 0
嗯,这似乎不是最优的!但至少它有效。
最后,在阅读博客文章后,上面的原始示例似乎有一个解决方法。在 VB.NET 中使用新的合并运算符 (If)...
Public Sub ActiveRecord_Find_By_NonKey_Returns_123_Fix()
Dim orders = Order.Find(Function(item As Order) If(item.EmployeeID, 0) = 1)
Assert.Equal(Of Integer)(123, orders.Count)
End Sub
生成此 WHERE
子句...
WHERE (COALESCE([t0].[EmployeeID], 0) = 1)
稍微更简洁的查询,尽管它与使用 ISNULL
之间存在差异与COALESCE
相反?也许只是在 SQL 版本之间的兼容性方面。
基本上,我希望第一个示例按原样工作。我还希望第二个示例继续工作,但生成更清晰的 SQL。
我想自己解决这个问题,但除了意识到 VB.NET 表达式树生成存在一些问题之外,还没有走得太远。
I've been trying to solve a little problem with VB.NET and the expression trees it likes to generate.
I have a simple test...
Public Sub ActiveRecord_Find_By_NonKey_Returns_123()
Dim orders = Order.Find(Function(item As Order) item.EmployeeID = 1)
Assert.Equal(Of Integer)(123, orders.Count)
End Sub
One would expect that to work but there is a problem with the item.EmployeeID = 1
bit and it is only a problem for VB.NET and not C#. VB.NET likes to be clever with the expression tree it compiles because of the fact item.EmployeeID
is nullable! (Read blog relating to this)
The problem is that expression item.EmployeeID = 1
gets wraps with a Convert
expression node. At which point the TSql generator gets completely lost and creates the following WHERE
clause.
WHERE ([t0].[EmployeeID] = 1) <> 0
That snippet tends to fail somewhat when it is executed on the database.
So that seems to be a bug in SubSonic. Unfortunately trying to work out how/where to fix it is doing my head in!
It does get more interesting though.
Public Sub ActiveRecord_Find_By_NonKey_Returns_123_Linq()
Dim orders = From item In Order.All Where item.EmployeeID = 1 Select item
Assert.Equal(Of Integer)(123, orders.Count)
End Sub
But its WHERE
clause is...
WHERE COALESCE(CASE WHEN (([t0].[EmployeeID] = 1)) THEN 1 ELSE 0 END, 0) <> 0
Well that seems sub-optimal! But at least it works.
Finally, there seems to be a work around for the original example above after reading the blog entry. Use the new coalesce operator (If) in VB.NET...
Public Sub ActiveRecord_Find_By_NonKey_Returns_123_Fix()
Dim orders = Order.Find(Function(item As Order) If(item.EmployeeID, 0) = 1)
Assert.Equal(Of Integer)(123, orders.Count)
End Sub
Which produces this WHERE
clause...
WHERE (COALESCE([t0].[EmployeeID], 0) = 1)
A slightly more concise query though it there a difference between using ISNULL
as opposed to COALESCE
? Perhaps only in terms of compatibility across versions of SQL.
Basically, I want the first example to work as it is. I'd also like the second example to keep working but produce much cleaner SQL.
I would like to fix this myself but haven't gotten very far other than realise I have some issues with VB.NETs expression tree generation.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论