2016-03-07 69 views
21

Ho sperimentato con query in LinqPad. tavolo Lot con una colonna Side char(1). Quando scrivo una query LINQ to SQL Lots.Where(l => l.Side == 'A'), produce il seguente SQLSQL diverso prodotto da 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 

Tuttavia, utilizzando Lots.Where(l => l.Side.Equals('A')), produce

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

sembrerebbe momento (sebbene semplificata) ispezione, che quest'ultimo sarebbe marginalmente più veloce, così come è non è necessario chiamare allo UNICODE.

Utilizzando int, smallint o varchar colonne non c'è differenza tra il prodotto con SQL == o .Equals, perché è char(1) e il corrispondente C# tipo char diverso?

C'è un modo per prevedere se un determinato tipo di colonna produrrà SQL diverso con le due forme di controllo di uguaglianza?

Edit:

Ho controllato ogni tipo supportato da MS SQL, e solo char(1) e nchar(1) mostrare questo comportamento. Entrambi sono rappresentati in LinqToSql dal tipo System.Char. Se è stata una decisione deliberata, quindi mi sarei aspettato lo stesso comportamento su binary(1), che potrebbe essere rappresentato da System.Byte (ma invece è System.Linq.Binary con una lunghezza di 1

Edit. 2: Nel caso in cui è rilevante, io sono utilizzando LINQPad per visualizzare l'SQL creato. Stavo supponendo che Linqpad usasse il LinqToSQL del sistema, ma oggi mi sono reso conto che quell'ipotesi poteva essere imperfetta

Modifica 3: ho eseguito un rapido progetto VS per testare il sistema LinqToSQL, e noi ottenere lo stesso risultato:

Codice:

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

uscita:

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 

E 'interessante notare che nella versione == 'A', il parametro viene passato come int, mentre nella versione .Equals, viene passato come char.

The dbml and table creation script are in this gist.

+0

Puoi pubblicare la mappatura DBML? – usr

+0

@usr era attraverso LINQPad, per il quale non so come recuperare la mappatura. – RoadieRich

risposta

12

V'è una certa documentation su questo:

non corrispondenti in SQL Server: tipi di carattere di lunghezza fissa. Transact-SQL distingue tra categorie Unicode e non Unicode e ha tre tipi distinti in ciascuna categoria: lunghezza fissa nchar/char, lunghezza variabile nvarchar/varchar e testo/testo di dimensioni maggiori. I tipi di caratteri a lunghezza fissa potrebbero essere associati al tipo CLR System.Char per recuperare i caratteri, ma in realtà non corrispondono allo stesso tipo nelle conversioni e nel comportamento.

E il codice sorgente L2S ha un solo posto che utilizza la stringa letterale "UNICODE":

enter image description here

Sembra che una condizione necessaria per la funzione di presentarsi è un nodo della struttura SqlUnary sintassi con tipo Convert:

enter image description here

non lo so come sei riuscito a soddisfare la condizione IsNumeric. Penso che tu abbia una mancata corrispondenza di tipo lì. La colonna è davvero mappata come System.Char?

La chiamata Equals probabilmente non attiva questo percorso di codice. Questo probabilmente è un bug L2S.

Equals è tradotto in più posizioni nel codice sorgente. Ecco uno di loro:

enter image description here

Sembra che questo ignora eventuali conversioni argomento. Non importa quale sia l'argomento. Questo probabilmente fallisce con una varietà di query (quindi è probabile che si tratti di un bug). Mi chiedo cosa succede se scrivi l.Side.Equals(1.2m). Immagino che questo si traduca letteralmente in SQL.


L'ho ora riprodotto. Mappare la colonna su string. Questo corregge l'SQL generato. Il piano di esecuzione mostra che è possibile ricercare un indice con l'SQL che viene generato.

+0

'l.Side.Equals (1.2m)' genera un 'InvalidCastException' (non può essere convertito dal tipo 'System.Decimal' per digitare 'System.Char'.) Prima che venga generato qualsiasi SQL - almeno, qualsiasi sql visibile senza scavando in interni. – RoadieRich

+0

Probabilmente dovrei aggiungere che sto usando LINQPad per visualizzare l'SQL creato, se fa alcuna differenza. Supponevo che LINQPad usasse la libreria L2S del sistema, ma mi sono reso conto che l'ipotesi era probabilmente errata. – RoadieRich

+0

LinqPad probabilmente utilizza i file binari non modificati. È possibile interrogare la mappatura tramite il sistema di metadati di runtime di L2S. C'è documentazione MSDN per questo. Ma sarebbe meglio se tu creassi un progetto VS per eseguire i tuoi test. In questo momento non so come procedere visto che LinqPad potrebbe usare tutti i tipi di strane mappature e tecniche invasive. – usr