2016-03-07 30 views
21

Eksperymentowałem z zapytaniami w LinqPad. stół Lot z kolumną Side char(1). Kiedy piszę LINQ do zapytania sql Lots.Where(l => l.Side == 'A'), produkuje następujące SQLRóżne SQL utworzone z Where (l => l.Side == "A") vs 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 

jednak użycie Lots.Where(l => l.Side.Equals('A')), produkuje

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

wydaje się na (choć naiwny) inspekcję, że ta ostatnia byłaby marginalnie szybsza, jak to robią musisz zadzwonić pod numer UNICODE.

Korzystanie int, smallint lub varchar kolumn nie ma różnicy pomiędzy produkowanego SQL z == lub .Equals, dlaczego char(1) i odpowiedni typ C# char inaczej?

Czy istnieje sposób przewidzieć, czy dany typ kolumny będzie produkować różne SQL z dwóch form kontroli równości?

Edit:

Sprawdziłem każdy rodzaj obsługiwanego przez MS SQL, a jedynie char(1) i nchar(1) pokazują to zachowanie. Oba są reprezentowane w LinqToSql przez typ System.Char. Jeśli to była przemyślana decyzja, to bym oczekiwał, że samo zachowanie na binary(1), które mogą być reprezentowane przez System.Byte (lecz jest System.Linq.Binary o długości 1

Edit. 2: W przypadku istotne jest, jestem Używając LINQPada do wyświetlenia utworzonego SQL, zakładałem, że Linqpad użyje LinqToSQL w systemie, ale zdałem sobie sprawę, że to założenie może być błędne

Edycja 3: Uruchomiłem szybki projekt VS, aby przetestować system LinqToSQL, a my uzyskać ten sam wynik:

Kod:

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(); 
} 

wyjściowa:

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 

Warto zwrócić uwagę, że w wersji == 'A' parametr jest przekazywana jako int, natomiast w wersji .Equals, jest przekazywana jako char.

The dbml and table creation script are in this gist.

+0

Czy możesz opublikować mapowanie DBML? – usr

+0

@usr to było przez LINQPad, dla którego nie wiem jak odzyskać mapowanie. – RoadieRich

Odpowiedz

12

Istnieją pewne documentation o tym:

niedopasowania SQL Server: Stałe typy znaków długości. Język Transact-SQL rozróżnia kategorie Unicode i inne niż Unicode i ma trzy odrębne typy w każdej kategorii: stała długość nchar/char, zmienna długość nvarchar/varchar i większy rozmiar ntext/text. Typy znaków o stałej długości mogą być mapowane do typu CLR System.Char w celu pobierania znaków, ale tak naprawdę nie odpowiadają temu samemu typowi konwersji i zachowania.

a kod źródłowy L2S ma tylko jedno miejsce, które wykorzystuje ciąg dosłownego "UNICODE":

enter image description here

Wydaje się, że warunkiem koniecznym dla funkcji, aby pokazać się jest SqlUnary składnia węzeł drzewa Typ Convert:

enter image description here

nie wiem w jaki sposób udało ci się spełnić warunek IsNumeric. Myślę, że masz tam niedopasowanie typu. Czy kolumna jest rzeczywiście mapowana jako System.Char?

Wywołanie Equals prawdopodobnie nie uruchamia tej ścieżki kodowej. Prawdopodobnie jest to błąd L2S.

Equals jest tłumaczone na wiele miejsc w kodzie źródłowym. Oto jeden z nich:

enter image description here

Wygląda na to omija wszelkie konwersje argument. Nie obchodzi go, czym jest argument. To prawdopodobnie kończy się niepowodzeniem z wieloma zapytaniami (więc jest to prawdopodobnie błąd). Zastanawiam się, co się stanie, jeśli napiszesz l.Side.Equals(1.2m). Myślę, że to przekłada się dosłownie na SQL.


Odtworzono go. Zamapuj kolumnę na string. Naprawia wygenerowany SQL. Plan wykonania pokazuje, że wyszukiwanie indeksu jest możliwe z generowanym SQL.

+0

'l.Side.Equals (1.2m)' rzuca 'InvalidCastException' (Nie można przekonwertować z typu" System.Decimal "na typ" System.Char ".) Przed wygenerowaniem jakiegokolwiek kodu SQL - przynajmniej widoczny sql bez kopanie w wewnętrzne. – RoadieRich

+0

Powinienem chyba dodać, że używam LINQPada do przeglądania utworzonego SQL, jeśli robi to jakąkolwiek różnicę. Zakładałem, że LINQPad użyje biblioteki L2S systemu, ale zdałem sobie sprawę, że to założenie było prawdopodobnie wadliwe. – RoadieRich

+0

LinqPad prawdopodobnie używa niezmodyfikowanych plików binarnych. Możesz zapytać mapowanie przez system metadanych środowiska wykonawczego L2S. Jest na to dokumentacja MSDN. Ale byłoby lepiej, gdybyś stworzył projekt VS do przeprowadzenia testów. W tej chwili nie wiem, jak postępować, biorąc pod uwagę, że LinqPad może używać wszelkiego rodzaju dziwnych mapowań i technik inwazyjnych. – usr