EF DateTime2 y el índice perdido

the bug hunting!

Hoy me han remitido un caso muy curioso de EF y el tratamiento de los campos DateTime. Básicamente, lo que me comentaban era que EF generaba una consulta incorrecta incluyendo un CAST a DateTime2, con lo cual hacía imposible la utilización del índice que la base de datos tenía sobre esta columna. Para seros sinceros, me sonó raro, puesto que es habitual ver el profiling de las consultas y me hubiera cantado a la primera, por lo que me puse a investigar el tema. Como siempre, lo primero es una aplicación de mínimos y por ello, nada más tonto que lo siguiente:

 

public class UnitOfWork
   :DbContext
 
   public IDbSet<Sample> Samples { get; set; }
 
ublic class Sample
 
   public int Id { get; set; }
   public DateTime Fecha { get; set; }

 

Ahora, solamente nos queda tirar una consulta y ver que se está ejecutando:

 

static void QueryWithoutCastToDateTime2()
{
    using (UnitOfWork uow = new UnitOfWork())
    {
        DateTime fechaInit = DateTime.Now.AddDays(-2);
        DateTime fechaFin = DateTime.Now;
        var result = uow.Samples.Where(d => fechaInit < d.Fecha && d.Fecha < fechaFin).ToList();
    }
}

 

exec sp_executesql N'SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[Fecha] AS [Fecha]
FROM [dbo].[Samples] AS [Extent1]

WHERE (@p__linq__0 < [Extent1].[Fecha]) AND ([Extent1].[Fecha] < @p__linq__1)',N'@p__linq__0 datetime2(7),@p__linq__1 datetime2(7)',@p__linq__0='2012-03-25 18:16:30.5246971',

@p__linq__1='2012-03-27 18:16:30.5251972'

 

Bien, perfecto.. No veo el problema que se comenta!!!!!!!!!  Revisando el código en el que aparecía este problema, buscando cualquier nota diferente como que estuviera forzando el tipo a DateTime2 etc.. nos dimos cuenta que la única diferencia era que en su ejemplo los campos eran Nullables. Por lo tanto, probamos a cambiar el ejemplo tonto anterior haciendo uso de DateTime?.

 

static void QueryWithCastToDateTime2()
{
    using (UnitOfWork uow = new UnitOfWork())
    {
        DateTime? fechaInit = DateTime.Now.AddDays(-2);
        DateTime? fechaFin = DateTime.Now;
        var result = uow.Samples.Where(d => fechaInit < d.Fecha && d.Fecha < fechaFin).ToList();
    }
}

Revisando la consulta con el profiler nos dimos cuenta de que efectivamente, si hacemos uso de DateTime? las consultas generan un CAST incorrecto que tira por tierra nuestros índices…

exec sp_executesql N'SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[Fecha] AS [Fecha]
FROM [dbo].[Samples] AS [Extent1]
WHERE 
(@p__linq__0 <  CAST( [Extent1].[Fecha] AS datetime2)) 
AND
( CAST( [Extent1].[Fecha] AS datetime2) < @p__linq__1)'
,N'@p__linq__0 datetime2(7),@p__linq__1 datetime2(7)',
@p__linq__0='2012-03-25 18:17:38.1257814',
@p__linq__1='2012-03-27 18:17:38.1262814'

 

Por supuesto esto es un bug, un bug que esperemos que arreglen en la siguiente versión, mientras tanto, ya sabéis, cuidado con los DateTime?.

P.d: Vizoso, gracias por reportar el bug!!

 

Saludos

Unai

Published 27/3/2012 18:52 por Unai
Comparte este post:
http://geeks.ms/blogs/unai/archive/2012/03/27/ef-datetime2-y-el-237-ndice-perdido.aspx

Comentarios

# re: EF DateTime2 y el índice perdido

Hola Unai,

La verdad que me has quitado parte de mi último capítulo de la "historia interminable" :). Pero bueno daremos una vuelta de tuerca a ver que pasa con otras situaciones de Null o por lo menos citamos el Warning de compilación:).

De todas formas aunque es un bug como comentas poco sentido tiene hacer mayor y menor que Null excepto que no encuentres nada y más si el campo no permite Null.

Saludos.

Tuesday, March 27, 2012 9:04 PM por Pedro Hurtado

# re: EF DateTime2 y el índice perdido

Bueno, es un bug porque estan haciendo algo en base a un tipo y no en la informacion del store.Si tiene mas o menos sentido en este caso no me importa mucho, se podria reproducir con escenarioa que si que lo tengan.

Unai

Tuesday, March 27, 2012 9:45 PM por Unai

# re: EF DateTime2 y el índice perdido

Hola,

Efectivamente que en este escenario no importa mucho y en otros puede importar y de lo lindo e incluso en este si el volumen de registros es grande, pero sentido creo que tiene menos y me explico.

Si tienes grabado un DateTime en tu store o bb.dd que no permite null que pintan dos variables DateTime definidas como Nullable y  preguntar por mayor y menor. En todo caso si fuese por igual algún sentido le vería a esa DateTime?.

Saludos.

Tuesday, March 27, 2012 10:00 PM por Pedro Hurtado

# re: EF DateTime2 y el índice perdido

Hola, si declaras

public DateTime Fecha { get; set; }

Se crea como:

[Fecha] [datetime] NOT NULL

Hasta aquí, es el ejemplo anterior.

Pero si declaras

public DateTime? Fecha { get; set; }

Se crea como:

   [Fecha] [datetime],

Y el resultado de la query sería:

exec sp_executesql N'SELECT

[Extent1].[Id] AS [Id],

[Extent1].[Fecha] AS [Fecha]

FROM [dbo].[Samples] AS [Extent1]

WHERE (@p__linq__0 < [Extent1].[Fecha]) AND ([Extent1].[Fecha] < @p__linq__1)',N'@p__linq__0 datetime2(7),@p__linq__1 datetime2(7)',@p__linq__0='2012-03-26 21:02:51.4361089',@p__linq__1='2012-03-28 21:02:51.4361089'

Por lo que, al mapear el tipo de CLR a un tipo de SQL Server, la única diferenca entre un DateTime y un DateTime? es que sea nullable o no, pero parece que si todos son nullables, no se ve en la necesidad de hacer ese CAST o no se produce el fallo.

También probando a especificar el tipo a mano con un:

public class SampleEntityConfiguration

       : EntityTypeConfiguration<Sample>

   {

       public SampleEntityConfiguration()

       {

           this.Property(l => l.Fecha)

               .HasColumnType("datetime2");

       }

   }

Crea el campo como:

[Fecha] [datetime2](7) NOT NULL,

Pero sigue haciendo:

exec sp_executesql N'SELECT

[Extent1].[Id] AS [Id],

[Extent1].[Fecha] AS [Fecha]

FROM [dbo].[Samples] AS [Extent1]

WHERE (@p__linq__0 <  CAST( [Extent1].[Fecha] AS datetime2)) AND ( CAST( [Extent1].[Fecha] AS datetime2) < @p__linq__1)',N'@p__linq__0 datetime2(7),@p__linq__1 datetime2(7)',@p__linq__0='2012-03-26 21:15:22.1000444',@p__linq__1='2012-03-28 21:15:22.1000444'

El comportamiento sigue siendo curioso.

Huelga decir que tener nullable sobre Fecha si va a ser índice de la tabla no tiene mucho sentido, pero Unai tiene razón, esto puede saltar en cualquier parte.

Saludos!

Wednesday, March 28, 2012 9:21 PM por Vicente García Diez

# re: EF DateTime2 y el índice perdido

Aprovecho este hilo para indicar un enlace también curioso acerca del comportamiento DateTime2 y SQL Server 2008 en concreto.

www.sqlservergeeks.com/.../sql-server-datetime-and-datetime2-behavior-have-you-seen-this

Monday, April 9, 2012 12:01 PM por Jorge Serrano