LINQ 和 VB.NET 以及编译的表达式树正在破坏 SubSonic 3

发布于 2024-08-03 16:22:49 字数 1857 浏览 3 评论 0原文

我一直在尝试解决 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 = 1Convert 表达式节点包裹。此时 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文