¿Cómo corregir el error de conversión de datetime2 fuera de rango usando DbContext y SetInitializer?

136

Estoy usando las API DbContext y Code First introducidas con Entity Framework 4.1.

El modelo de datos utiliza tipos de datos básicos como stringy DateTime. La única anotación de datos que estoy usando en algunos casos es [Required], pero eso no está en ninguna de las DateTimepropiedades. Ejemplo:

public virtual DateTime Start { get; set; }

La subclase DbContext también es simple y se ve así:

public class EventsContext : DbContext
{
    public DbSet<Event> Events { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Event>().ToTable("Events");
    }
}

El inicializador establece las fechas en el modelo a valores razonables en este año o el próximo.

Sin embargo, cuando ejecuto el inicializador, aparece este error en context.SaveChanges():

La conversión de un tipo de datos datetime2 a un tipo de datos datetime dio como resultado un valor fuera de rango. La instrucción se ha terminado.

No entiendo por qué sucede esto en absoluto porque todo es muy simple. Tampoco estoy seguro de cómo solucionarlo ya que no hay un archivo edmx para editar.

¿Algunas ideas?

Alex Angas
fuente
2
¿Se puede utilizar el Analizador de SQL para ver las instrucciones de inserción / actualización de SQL? Es bastante difícil decir qué está pasando aquí: no vemos su inicializador o entidades. SQL Profiler te ayudará mucho a localizar el problema.
Ladislav Mrnka
1
En mi caso, agregué un campo a una tabla y edité el formulario, olvidé actualizar el enlace Incluye y mi campo se configuró en NULL. Entonces el error ayudó a corregir mi descuido.
strattonn

Respuestas:

186

Debe asegurarse de que Start sea mayor o igual que SqlDateTime.MinValue (1 de enero de 1753); de manera predeterminada, Start es igual a DateTime.MinValue (1 de enero de 0001).

andygjp
fuente
14
Había dejado algunos de mis objetos de inicializador sin una fecha establecida, por lo que habría estado predeterminado a DateTime.MinValue.
Alex Angas
Yo también hice eso ^. Se agregó un campo de fecha personalizado al objeto ApplicationUser de identidad asp.net y luego se olvidó de inicializarlo en algo que tuviera sentido. : (
Mike Devenney
En mi caso, el problema estaba en la fecha mínima, 01/01/0001 estaba generando el error.
Machado
¿Cómo puedo verificar dateTime.minvalue?
Anabeil
1
Un poco tarde, pero @Anabeil debería poder simplemente Console.WriteLine (DateTime.MinValue) en la ventana inmediata de VS / linqpad
SIRHAMY
22

Sencillo. Primero en su código, establezca el tipo de DateTime en DateTime ?. Para que pueda trabajar con el tipo DateTime anulable en la base de datos. Ejemplo de entidad:

public class Alarme
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        public DateTime? DataDisparado { get; set; }//.This allow you to work with nullable datetime in database.
        public DateTime? DataResolvido { get; set; }//.This allow you to work with nullable datetime in database.
        public long Latencia { get; set; }

        public bool Resolvido { get; set; }

        public int SensorId { get; set; }
        [ForeignKey("SensorId")]
        public virtual Sensor Sensor { get; set; }
    }
Ulisses Ottoni
fuente
66
ese no es siempre el caso. {1/1/0001 12:00:00 AM} también hace surgir este error
eran otzap
20

En algunos casos, DateTime.MinValue(o equivalentemente,default(DateTime) ) se usa para indicar un valor desconocido.

Este método de extensión simple puede ayudar a manejar tales situaciones:

public static class DbDateHelper
{
    /// <summary>
    /// Replaces any date before 01.01.1753 with a Nullable of 
    /// DateTime with a value of null.
    /// </summary>
    /// <param name="date">Date to check</param>
    /// <returns>Input date if valid in the DB, or Null if date is 
    /// too early to be DB compatible.</returns>
    public static DateTime? ToNullIfTooEarlyForDb(this DateTime date)
    {
        return (date >= (DateTime) SqlDateTime.MinValue) ? date : (DateTime?)null;
    }
}

Uso:

 DateTime? dateToPassOnToDb = tooEarlyDate.ToNullIfTooEarlyForDb();
Kjartan
fuente
13

Puede hacer que el campo sea anulable, si eso se adapta a sus preocupaciones específicas de modelado. Una fecha nula no se convertirá en una fecha que no se encuentre dentro del rango del tipo DateTime de SQL como lo haría un valor predeterminado. Otra opción es asignar explícitamente a un tipo diferente, tal vez con,

.HasColumnType("datetime2")
Brian Kretzler
fuente
12

Aunque esta pregunta es bastante antigua y ya hay excelentes respuestas, pensé que debería poner una más que explica 3 enfoques diferentes para resolver este problema.

1er enfoque

Asigna explícitamente la DateTimepropiedad public virtual DateTime Start { get; set; }a datetime2en la columna correspondiente de la tabla. Porque por defecto EF lo asignará a datetime.

Esto se puede hacer mediante una fluida API o anotación de datos.

  1. API fluida

    En DbContext, la clase anula OnModelCreatingy configura la propiedad Start(por razones explicativas, es una propiedad de la clase EntityClass).

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        //Configure only one property 
        modelBuilder.Entity<EntityClass>()
            .Property(e => e.Start)
            .HasColumnType("datetime2");
    
       //or configure all DateTime Preperties globally(EF 6 and Above)
        modelBuilder.Properties<DateTime>()
            .Configure(c => c.HasColumnType("datetime2"));
    }
  2. Anotación de datos

    [Column(TypeName="datetime2")]
    public virtual DateTime Start { get; set; }

2º enfoque

Inicialice Starta un valor predeterminado en EntityClass constructor. Esto es bueno como si por alguna razón el valor de Startno se establece antes de guardar la entidad en el inicio de la base de datos siempre tendrá un valor predeterminado. Asegúrese de que el valor predeterminado sea mayor o igual que SqlDateTime.MinValue (del 1 de enero de 1753 al 31 de diciembre de 9999)

public class EntityClass
{
    public EntityClass()
    {
        Start= DateTime.Now;
    }
    public DateTime Start{ get; set; }
}

3er enfoque

Hacer Startque sea de tipo nullable DateTime -note ? after DateTime-

public virtual DateTime? Start { get; set; }

Para más explicaciones lea esta publicación

objetivo
fuente
8

Si sus DateTimepropiedades son anulables en la base de datos, asegúrese de usarlas DateTime?para las propiedades de objeto asociadas o EF pasará DateTime.MinValuepor valores no asignados que están fuera del rango de lo que puede manejar el tipo de fecha y hora de SQL.

Christopher King
fuente
8

Mi solución fue cambiar todas las columnas de fecha y hora a datetime2, y usar datetime2 para cualquier columna nueva. En otras palabras, EF usa datetime2 por defecto. Agregue esto al método OnModelCreating en su contexto:

modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));

Que obtendrá todo el DateTime y DateTime? propiedades en todas sus entidades.

Ogglas
fuente
3

inicializar la propiedad Start en el constructor

Start = DateTime.Now;

Esto funcionó para mí cuando intentaba agregar algunos campos nuevos a la tabla de usuarios de ASP .Net Identity Framework (AspNetUsers) usando Code First. Actualicé el Class - ApplicationUser en IdentityModels.cs y agregué un campo lastLogin de tipo DateTime.

public class ApplicationUser : IdentityUser
    {
        public ApplicationUser()
        {
            CreatedOn = DateTime.Now;
            LastPassUpdate = DateTime.Now;
            LastLogin = DateTime.Now;
        }
        public String FirstName { get; set; }
        public String MiddleName { get; set; }
        public String LastName { get; set; }
        public String EmailId { get; set; }
        public String ContactNo { get; set; }
        public String HintQuestion { get; set; }
        public String HintAnswer { get; set; }
        public Boolean IsUserActive { get; set; }

        //Auditing Fields
        public DateTime CreatedOn { get; set; }
        public DateTime LastPassUpdate { get; set; }
        public DateTime LastLogin { get; set; }
    }
Abhinav Bhandawat
fuente
2

Según la respuesta del usuario @ andygjp, es mejor si anula la base Db.SaveChanges() método y agrega una función para anular cualquier fecha que no se encuentre entre SqlDateTime.MinValue y SqlDateTime.MaxValue.

Aquí está el código de ejemplo

public class MyDb : DbContext
{
    public override int SaveChanges()
    {
        UpdateDates();
        return base.SaveChanges();
    }

    private void UpdateDates()
    {
        foreach (var change in ChangeTracker.Entries().Where(x => (x.State == EntityState.Added || x.State == EntityState.Modified)))
        {
            var values = change.CurrentValues;
            foreach (var name in values.PropertyNames)
            {
                var value = values[name];
                if (value is DateTime)
                {
                    var date = (DateTime)value;
                    if (date < SqlDateTime.MinValue.Value)
                    {
                        values[name] = SqlDateTime.MinValue.Value;
                    }
                    else if (date > SqlDateTime.MaxValue.Value)
                    {
                        values[name] = SqlDateTime.MaxValue.Value;
                    }
                }
            }
        }
    }
}

Tomado del comentario del usuario @ sky-dev en https://stackoverflow.com/a/11297294/9158120

Hamza Khanzada
fuente
1

Tuve el mismo problema y en mi caso estaba configurando la fecha a la nueva DateTime () en lugar de DateTime.

sam
fuente
0

En mi caso, esto sucedió cuando utilicé la entidad y la tabla sql tiene el valor predeterminado de datetime == getdate (). Entonces, ¿qué hice para establecer un valor para este campo?

Ahmad Moayad
fuente
0

Estoy usando Database First y cuando me ocurrió este error, mi solución fue forzar ProviderManifestToken = "2005" en el archivo edmx (haciendo que los modelos sean compatibles con SQL Server 2005). No sé si algo similar es posible para Code First.

Sérgio Azevedo
fuente
0

Una línea arregla esto:

modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));

Entonces, en mi código, agregué:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));
}

Agregar esa línea a la subclase DBContext anula la sección vacía OnModelCreating debería funcionar.

Howie Krauth
fuente
0

En mi caso, después de algunas refactorizaciones en EF6, mis pruebas fallaban con el mismo mensaje de error que el póster original, pero mi solución no tenía nada que ver con los campos DateTime.

Solo me faltaba un campo obligatorio al crear la entidad. Una vez que agregué el campo faltante, el error desapareció. ¿Mi entidad tiene dos DateTime? campos pero no eran el problema.

Enano gris
fuente