从Where(l => l.Side =='A')vs Where(l => l.Side.Equals('A')

时间:2016-03-07 21:33:00

标签: c# linq-to-sql linqpad

我一直在试验LinqPad中的查询。我们有一个表Lot,其中包含Side char(1)列。当我将linq写入sql查询Lots.Where(l => l.Side == 'A')时,它会生成以下SQL

-- Region Parameters
DECLARE @p0 Int = 65
-- EndRegion
SELECT ..., [t0].[Side], ...
FROM [Lot] AS [t0]
WHERE UNICODE([t0].[Side]) = @p0

然而,使用Lots.Where(l => l.Side.Equals('A')),它会产生

-- Region Parameters
DECLARE @p0 Char(1) = 'A'
-- EndRegion
SELECT ..., [t0].[Side], ...
FROM [Lot] AS [t0]
WHERE [t0].[Side] = @p0

它会出现(尽管是天真的)检查,后者会稍快一些,因为它不需要调用UNICODE

使用intsmallintvarchar列,生成的SQL与==.Equals之间没有区别,为什么char(1)和相应的C#类型char不同?

有没有办法预测给定的列类型是否会产生两种形式的相等检查?

编辑:

我检查了MS SQL支持的每种类型,只有char(1)nchar(1)显示此行为。两者都以System.Char类型在LinqToSql中表示。如果这是一个深思熟虑的决定,那么我会期望在binary(1)上有相同的行为,可以用System.Byte表示(但System.Linq.Binary的长度为1

编辑2:如果它是相关的,我使用LINQPad来查看创建的SQL。我假设Linqpad会使用系统的LinqToSQL,但我今天意识到这个假设可能存在缺陷。

编辑3:我运行了一个快速的VS项目来测试系统LinqToSQL,我们得到了相同的结果:

代码:

static void Main(string[] args)
{
    var db = new DataClasses1DataContext {Log = Console.Out};
    Console.Out.WriteLine("l.Side == 'A'");
    Console.Out.WriteLine("=============");
    Console.Out.WriteLine();
    foreach (Lot ll in db.Lots.Where(l => l.Side == 'A'))
    {
        break;
    }
    Console.Out.WriteLine();
    Console.Out.WriteLine("---------------------------------------");
    Console.Out.WriteLine();

    Console.Out.WriteLine("l.Side.Equals('A')");
    Console.Out.WriteLine("==================");
    Console.Out.WriteLine();
    foreach (Lot ll in db.Lots.Where(l => l.Side.Equals('A')))
    {
        break;
    }
    Console.In.Read();
}

输出:

l.Side == 'A'
=============

SELECT ..., [t0].[Side], ...
FROM [dbo].[Lot] AS [t0]
WHERE UNICODE([t0].[Side]) = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [65]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.6.1532.0


---------------------------------------

l.Side.Equals('A')
==================

SELECT ..., [t0].[Side], ...
FROM [dbo].[Lot] AS [t0]
WHERE [t0].[Side] = @p0
-- @p0: Input Char (Size = 1; Prec = 0; Scale = 0) [A]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.6.1532.0

值得注意的是,在== 'A'版本中,参数作为int传递,而在.Equals版本中,参数传递为char

The dbml and table creation script are in this gist.

1 个答案:

答案 0 :(得分:12)

对此有一些documentation

  

SQL Server中的不匹配:   固定长度的字符类型。 Transact-SQL区分Unicode和非Unicode类别,并且在每个类别中有三种不同的类型:固定长度nchar / char,可变长度nvarchar / varchar和更大的ntext / text。固定长度字符类型可以映射到CLR System.Char类型以检索字符,但它们实际上并不对应于转换和行为中的相同类型。

L2S源代码只有一个地方使用字符串文字"UNICODE"

enter image description here

看起来函数显示的必要前提条件是类型为SqlUnary的{​​{1}}语法树节点:

enter image description here

我不知道你是如何设法满足Convert条件的。我认为你的类型不匹配。该列是否真的映射为IsNumeric

System.Char调用可能不会触发此代码路径。这可能是一个L2S错误。

Equals在源代码中的多个位置进行了翻译。这是其中之一:

enter image description here

看起来这会绕过任何参数转换。它并不关心论证是什么。这可能会因各种查询而失败(因此可能存在错误)。我想知道如果你写Equals会发生什么。我想这会直接翻译成SQL。

我现在已经复制了它。将列映射到l.Side.Equals(1.2m)。这可以修复生成的SQL。执行计划显示可以使用正在生成的SQL进行索引搜索。