从Where(l => l.Side ==’A’)vs Where(l => l.Side.Equals(’A’)生成不同的SQL

我一直在试验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不同?

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

编辑:

我检查了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传递。

dbml和表创建脚本在这个要点中。

有一些关于此的文档 :

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

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

在此处输入图像描述

看来函数显示的必要前提条件是类型为ConvertSqlUnary语法树节点:

在此处输入图像描述

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

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

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

在此处输入图像描述

看起来这绕过了任何参数转换。 它并不关心论证是什么。 这可能会因各种查询而失败(因此可能是一个错误)。 我想知道如果你写l.Side.Equals(1.2m)会发生什么。 我想这可以转化为SQL。


我现在转载它。 将列映射到string 。 这可以修复生成的SQL。 执行计划显示可以使用正在生成的SQL进行索引搜索。