2016-03-07 193 views
21

我在LinqPad中试验过不同的查询。表Lot与列Side char(1)。当我写的LINQ to SQL查询Lots.Where(l => l.Side == 'A'),它产生下面的SQLWhere Where(l => l.Side =='A')与Where Where(l => l.Side.Equals('A')

-- 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列有与==.Equals产生的SQL没有什么区别,为什么char(1)和相应的C#类型char不同?

有什么办法来预测给定的列类型是否会产生与两种形式的相等性检查不同的SQL?

编辑:

我已经检查通过MS SQL支持所有类型的,只有char(1)nchar(1)显示此行为。在LinqToSql中,两者都由System.Char类型表示。如果这是一个深思熟虑的决定,那么我本来期望在binary(1)相同的行为,这可能是System.Byte表示(反而是System.Linq.Binary1长度

编辑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.

+0

你可以发布DBML映射吗? – usr

+0

@usr这是通过LINQPad,为此我不知道如何检索映射。 – RoadieRich

回答

12

有一些documentation一下:

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

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

enter image description here

看来,对于展现出来的功能的必要前提是与SqlUnary语法树节点类型Convert

enter image description here

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

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

Equals被翻译成多个地方的源代码。这里是其中的一个:

enter image description here

它看起来像这样绕过任何参数转换。它并不关心论点是什么。这可能会因各种查询而失败(所以这可能是一个错误)。我想知道如果你写l.Side.Equals(1.2m)会发生什么。我想这从字面上翻译成SQL。


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

+0

'l.Side.Equals(1.2m)'在生成任何SQL之前抛出一个'InvalidCastException'(无法从类型'System.Decimal'转换为'System.Char'。) - 至少,没有任何可见的sql挖入内部。 – RoadieRich

+0

我应该补充一点,我使用LINQPad来查看创建的SQL,如果它有任何区别的话。我假设LINQPad会使用系统L2S库,但我意识到这个假设可能有缺陷。 – RoadieRich

+0

LinqPad可能使用未修改的二进制文件。您可以通过L2S的运行时元数据系统查询映射。有关于此的MSDN文档。但是如果你创建了一个VS项目来运行你的测试会更好。现在我不知道如何继续LinqPad可以使用各种奇怪的映射和入侵技术。 – usr