c# - fromsql - Diferentes SQL producidos a partir de Where(l=> l.Side== ''A'') vs Where(l=> l.Side.Equals(''A'')
entity framework core no tracking (1)
He estado experimentando con consultas en LinqPad. Tenemos una mesa Lot
con una columna Side char(1)
. Cuando escribo una consulta de linq a SQL Lots.Where(l => l.Side == ''A'')
, produce el siguiente SQL
-- Region Parameters
DECLARE @p0 Int = 65
-- EndRegion
SELECT ..., [t0].[Side], ...
FROM [Lot] AS [t0]
WHERE UNICODE([t0].[Side]) = @p0
Sin embargo, al usar 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
Después de una inspección (aunque sea ingenua), parecería que este último sería marginalmente más rápido, ya que no necesita la llamada a UNICODE
.
Usando columnas int
, smallint
o varchar
no hay diferencia entre el SQL producido con ==
o .Equals
, ¿por qué es diferente char(1)
y el tipo de char(1)
C # correspondiente?
¿Hay alguna manera de predecir si un tipo de columna dado producirá SQL diferente con las dos formas de verificación de igualdad?
Editar:
He comprobado todos los tipos soportados por MS SQL, y solo char(1)
y nchar(1)
muestran este comportamiento. Ambos están representados en LinqToSql por el tipo System.Char
. Si fue una decisión deliberada, entonces habría esperado el mismo comportamiento en binary(1)
, que podría ser representado por System.Byte
(pero en su lugar es System.Linq.Binary
con una longitud de 1
.
Edición 2: en caso de que sea relevante, estoy usando LINQPad para ver el SQL creado. Asumía que Linqpad usaría LinqToSQL del sistema, pero me di cuenta hoy que esa suposición podría ser defectuosa.
Edición 3: Ejecuté un proyecto VS rápido para probar el sistema LinqToSQL, y obtenemos el mismo resultado:
Código:
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();
}
Salida:
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
Es interesante observar que en la versión == ''A''
, el parámetro se pasa como un int
, mientras que en la versión .Equals
, se pasa como char
.
Hay documentación sobre esto:
No coinciden en SQL Server: tipos de caracteres de longitud fija. Transact-SQL distingue entre categorías Unicode y no Unicode y tiene tres tipos distintos en cada categoría: nchar / char de longitud fija, nvarchar / varchar de longitud variable y ntext / text de mayor tamaño. Los tipos de caracteres de longitud fija se pueden asignar al tipo CLR System.Char para recuperar caracteres, pero en realidad no corresponden al mismo tipo en conversiones y comportamiento.
Y el código fuente L2S tiene solo un lugar que usa la cadena literal "UNICODE"
:
Parece que una condición previa necesaria para que aparezca la función es un nodo del árbol de sintaxis SqlUnary
con el tipo Convert
:
No sé cómo IsNumeric
satisfacer la condición IsNumeric
. Creo que tienes un desajuste de tipo allí. ¿La columna está realmente mapeada como System.Char
?
La llamada Equals
probablemente no active esta ruta de código. Esto probablemente es un error L2S.
Equals
se traduce en varios lugares en el código fuente. Aqui esta uno de ellos:
Parece que esto pasa por alto cualquier conversión de argumento. No le importa cuál es el argumento. Esto probablemente falla con una variedad de consultas (por lo que es probable que sea un error). Me pregunto qué pasará si escribe l.Side.Equals(1.2m)
. Supongo que esto se traduce literalmente a SQL.
Ahora lo he reproducido. Asigna la columna a la string
. Esto corrige el SQL generado. El plan de ejecución muestra que es posible una búsqueda de índice con el SQL que se está generando.