EF 4.0 Performance Tips #8

Sin lugar a dudas este es el tip de rendimiento más rebuscado de todos los visto hasta ahora, alguno podrá decir que ya los había un poco rebuscados, pero bueno, esta es mi opinión. Para tratar de explicarnos nos pondremos en situación. Supongamos que tenemos una tabla con una columna de tipo varchar, es decir, una columna no unicode, tal y como podría ser la siguiente:

CREATE TABLE [dbo].[NonUnicodeTest](
    [idTest] [int] NOT NULL,
    [field] [varchar](10) NOT NULL,
CONSTRAINT [PK_NonUnicodeTest] PRIMARY KEY CLUSTERED
(
    [idTest] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

 

Los que conocéis SQL sabéis que si realizamos una consulta con un filtro como WHERE field=N’ valor del campo’ podríamos tener un problema de NO uso de índices ya que esta consulta no es SARGABLE, hay una conversion de valor no unicode a valores unicode. Lo lógico es que si estamos usando Entity Framework el se ocupe por nosotros de no cometer estos errores, vamos a comprobarlo.

Si ejecutamos la siguiente consulta en Linq To Entities;

 

El SQL que llega a la base de datos es el siguiente:

 

exec sp_executesql N’SELECT
[Extent1].[idTest] AS [idTest],
[Extent1].[field] AS [field]
FROM [dbo].[NonUnicodeTest] AS [Extent1]
WHERE [Extent1].[field] = @p__linq__0′,N’@p__linq__0 varchar(8000)’,@p__linq__0=’field value’

 

Efectivamente, por ahora todo va bien, ADO.NET EF  resuelve este problema de forma correcta, es más que ese campo sea unicode o no es una atributo de la propiedad en el modelo conceptual, CSDL.

 

Untitled

Todo hace indicar que todo funcionará de forma correcta y este tipo de casuísticas está bien resuelto. Sin embargo vamos a probar con dos consultas adicionales:

 

Si examinamos las trazas con un profiler de SQL para estas dos consultas veremos los siguientes resultados, en el mismo orden.

 

exec sp_executesql N’SELECT
[Extent1].[idTest] AS [idTest],
[Extent1].[field] AS [field]
FROM [dbo].[NonUnicodeTest] AS [Extent1]
WHERE [Extent1].[field] IN (@p__linq__0,@p__linq__1)’,N’@p__linq__0 nvarchar(4000),@p__linq__1 nvarchar(4000)’,@p__linq__0=N’field1′,@p__linq__1=N’field2′

 

 

exec sp_executesql N’SELECT
[Extent1].[idTest] AS [idTest],
[Extent1].[field] AS [field]
FROM [dbo].[NonUnicodeTest] AS [Extent1]
WHERE ([Extent1].[field] = @p__linq__0) AND ([Extent1].[field] = @p__linq__1)’,N’@p__linq__0 varchar(8000),@p__linq__1 varchar(8000)’,@p__linq__0=’field1′,@p__linq__1=’field2′

 

Fijese que ya hemos puesto en negrita las diferencias, en la sentencia OR Entity Framework no es capaz  de reconocer el patrón de unicode/no unicode y aplicar el face correspondiente. Desde luego, esto es un bug en toda regla, seguramente porque la forma de analizar el CQT no es el mismo para la expresion OR que para la expresion AND.

 

Por suerte, disponemos de una forma de resolver este problema y es mediante una Model Defined Function. las mismas que vimos en un post anterior. De la misma forma que podemos crear nuevas funciones definidas en el modelo EF pone a nuestra disposición un conjunto de ellas out-of-box. Estas funciones las podemos encontrar dentro de las clases EntityFunctions y SqlFunctions, en concreto EntityFunctions nos provee de una función definida en el modelo con el nombre AsNonUnicode. Su utilización se muestra como se ve a continuación:

 

El resultado, despues del uso de EntityFunctions ya resulta el que esperamos:

 

exec sp_executesql N’SELECT
[Extent1].[idTest] AS [idTest],
[Extent1].[field] AS [field]
FROM [dbo].[NonUnicodeTest] AS [Extent1]
WHERE [Extent1].[field] IN (@p__linq__0,@p__linq__1)’,N’@p__linq__0 varchar(8000),@p__linq__1 varchar(8000)’,@p__linq__0=’field1′,@p__linq__1=’field2′

 

Saludos

Unai Zorrilla Castro

5 comentarios sobre “EF 4.0 Performance Tips #8”

  1. Hola Unai – gran post
    Hace poco me encontre con este mismo problema en un entorno de produccion.

    El problema se produccia tanto con Linq como con ObjectQuery. Al final lo solucione con ObjectQuery pero construyendo el ESQL a «pelo».

    No se me hubiera ocurrido que tuviera que ver con los operadores AND y OR.

    Saludos

  2. Tremendo tip tio.. curiosisimo el bug, pero sobre todo la solución: he de confesar que no conocia la función AsNonUnicode.

    Como siempre, genial! Un abrazote tio!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *