gpt4 book ai didi

c# - 使用 LINQ 时 VB TryCast 和 C# "as"运算符的区别

转载 作者:行者123 更新时间:2023-11-30 12:53:55 24 4
gpt4 key购买 nike

我有一个 LINQ 查询来检索整数列的最大值。该列在数据库中定义为 NOT NULL。但是,在 SQL 中使用 MAX 聚合函数时,如果查询没有返回任何行,您将得到 NULL 结果。

这是我针对 Northwind 数据库使用的示例 LINQ 查询,以演示我在做什么。

var maxValue = (from p in nw.Products 
where p.ProductID < 0
select p.ProductID as int?).Max();

C# 正确解析了这个查询,并且 maxValue 的类型是 int?。此外,生成的 SQL 是完美的:

SELECT MAX([t0].[ProductID]) AS [value]
FROM [Products] AS [t0]
WHERE [t0].[ProductID] < @p0

问题是,如何使用 VB.NET 对此进行编码并获得相同的结果?如果我直接翻译:

dim maxValue = (from p in Products 
where p.ProductID < 0
select TryCast(p.ProductID, integer?)).Max()

我遇到编译错误。 TryCast 仅适用于引用类型,不适用于值类型。 TryCast 和“as”在这方面略有不同。 C# 用装箱做一些额外的工作来处理值类型。所以,我的下一个解决方案是使用 CType 而不是 TryCast:

dim maxValue = (from p in Products 
where p.ProductID > 0
select CType(p.ProductID, integer?)).Max()

这有效,但它会生成以下 SQL:

SELECT MAX([t1].[value]) AS [value]
FROM (
SELECT [t0].[ProductID] AS [value], [t0].[ProductID]
FROM [Products] AS [t0]
) AS [t1]
WHERE [t1].[ProductID] > @p0

虽然这是正确的,但不是很干净。诚然,在这种特殊情况下,SQL Server 可能会将查询优化为与 C# 版本相同,我可以设想情况可能并非如此。有趣的是,在 C# 版本中,如果我使用普通转换(即 (int?)p.ProductID)而不是使用“as”运算符,我会得到与 VB 版本相同的 SQL。

有谁知道是否有办法在 VB 中为此类查询生成最佳 SQL?

最佳答案

简答:可以。

然后是长答案:

我认为您可以执行此操作的唯一方法是显式创建包含 TypeAs 转换的 lambda。您可以使用以下扩展方法来帮助您:

<Extension()> _
Public Module TypeAsExtensions
<Extension()> _
Public Function SelectAs(Of TElement, TOriginalType, TTargetType)( _
ByVal source As IQueryable(Of TElement), _
ByVal selector As Expression(Of Func(Of TElement, TOriginalType))) _
As IQueryable(Of TTargetType)

Return Queryable.Select(source, _
Expression.Lambda(Of Func(Of TElement, TTargetType))( _
Expression.TypeAs(selector.Body, GetType(TTargetType)), _
selector.Parameters(0)))
End Function

<Extension()> _
Public Function SelectAsNullable(Of TElement, TType As Structure)( _
ByVal source As IQueryable(Of TElement), _
ByVal selector As Expression(Of Func(Of TElement, TType))) _
As IQueryable(Of TType?)
Return SelectAs(Of TElement, TType, TType?)(source, selector)
End Function
End Module

SelectAs 将为任何 T 生成 TryCast(value, T),包括 Integer? .

要使用这个,你会说

Dim maxValue = Products _
.Where(Function(p) p.ProductID < 0) _
.SelectAsNullable(Function(p) p.ProductID) _
.Max()

它不是很漂亮,但它确实有效。 (这会生成与 C# 相同的查询。)只要您不在子查询中调用 SelectAsNullable 就可以了。

另一种选择是使用

Dim maxValue = (From p In Products _
Where p.ProductID < 0
Select p.ProductID) _
.SelectAsNullable(Function(id) id) _
.Max()

这个问题是你得到一个双重选择,即,

from p in Products 
where p.ProductID < 0
select p.ProductID
select p.ProductID as int?

用 C# 的说法。引用可能的 LINQ to SQL 仍然为此生成子查询。

无论如何,为此您可以创建一个额外的扩展方法

<Extension()> _
Public Function SelectAsNullable(Of TType As Structure)( _
ByVal source As IQueryable(Of TType)) _
As IQueryable(Of TType?)
Return SelectAs(Of TType, TType, TType?)(source, Function(x) x)
End Function

进一步简化 LINQ 查询

Dim maxValue = (From p In Products _
Where p.ProductID < 0
Select p.ProductID) _
.SelectAsNullable() _
.Max()

但正如我所说,这取决于 LINQ 提供程序。

关于c# - 使用 LINQ 时 VB TryCast 和 C# "as"运算符的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1297297/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com