Come correggere l'errore di conversione fuori intervallo di datetime2 utilizzando DbContext e SetInitializer?


136

Sto usando le API DbContext e Code First introdotte con Entity Framework 4.1.

Il modello di dati utilizza tipi di dati di base come stringe DateTime. L'unica annotazione dei dati che sto usando in alcuni casi è [Required], ma non è presente in nessuna delle DateTimeproprietà. Esempio:

public virtual DateTime Start { get; set; }

Anche la sottoclasse DbContext è semplice e si presenta come:

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

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

L' inizializzatore imposta le date nel modello su valori sensibili sia quest'anno che l'anno prossimo.

Tuttavia, quando eseguo l'inizializzatore, ottengo questo errore su context.SaveChanges():

La conversione di un tipo di dati datetime2 in un tipo di dati datetime ha prodotto un valore fuori range. La dichiarazione è stata chiusa.

Non capisco perché questo accada affatto perché tutto è così semplice. Inoltre, non sono sicuro di come risolverlo poiché non esiste un file edmx da modificare.

Qualche idea?


2
È possibile utilizzare SQL Profiler per visualizzare le istruzioni SQL di inserimento / aggiornamento? È abbastanza difficile dire cosa sta succedendo qui: non vediamo il tuo inizializzatore o entità. SQL Profiler ti aiuterà molto a localizzare il problema.
Ladislav Mrnka,

1
Nel mio caso avevo aggiunto un campo a una tabella e un modulo di modifica, ho dimenticato di aggiornare il Bind Include e il mio campo veniva impostato su NULL. Quindi l'errore mi ha aiutato a correggere la mia svista.
Strattonn,

Risposte:


186

Devi assicurarti che Start sia maggiore o uguale a SqlDateTime.MinValue (1 gennaio 1753) - per impostazione predefinita Start è uguale a DateTime.MinValue (1 gennaio 0001).


14
Avevo lasciato alcuni dei miei oggetti di inizializzazione senza una data prestabilita, quindi avrebbe impostato automaticamente DateTime.MinValue.
Alex Angas,

L'ho fatto anch'io ^. Aggiunto un campo data personalizzato all'oggetto ApplicationUser di identità asp.net, quindi ho dimenticato di inizializzarlo su qualcosa di sensato. : (
Mike Devenney,

Nel mio caso, il problema era nella data minima, 01/01/0001 stava generando l'errore.
Machado,

come posso controllare dateTime.minvalue?
Anabeil,

1
Un po 'in ritardo, ma @Anabeil dovresti riuscire solo a Console.WriteLine (DateTime.MinValue) nella finestra immediata di VS / linqpad
SIRHAMY

22

Semplice. Prima sul tuo codice, imposta il tipo di DateTime su DateTime ?. Quindi puoi lavorare con il tipo DateTime nullable nel database. Esempio di entità:

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

6
non è sempre così. {1/1/0001 12:00:00 AM} fa emergere anche questo errore
eran otzap,

20

In alcuni casi, DateTime.MinValue(o equivalentemente, default(DateTime)) viene utilizzato per indicare un valore sconosciuto.

Questo semplice metodo di estensione può aiutare a gestire tali situazioni:

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

13

Puoi rendere il campo nulla, se adatto alle tue specifiche preoccupazioni di modellazione. Una data nulla non sarà forzata a una data che non rientri nell'intervallo del tipo DateTime SQL come farebbe un valore predefinito. Un'altra opzione è quella di mappare esplicitamente un tipo diverso, magari con,

.HasColumnType("datetime2")

12

Anche se questa domanda è piuttosto vecchia e ci sono già ottime risposte, ho pensato di doverne aggiungere un'altra che spieghi 3 diversi approcci per risolvere questo problema.

1o approccio

Esplicitamente mappare DateTimestruttura public virtual DateTime Start { get; set; }a datetime2nella corrispondente colonna della tabella. Perché di default EF lo mapperà su datetime.

Questo può essere fatto tramite API fluente o annotazione dei dati.

  1. API fluente

    Nella classe DbContext sostituisci OnModelCreatinge configura la proprietà Start(per motivi di spiegazione è una proprietà della classe 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. Annotazione dei dati

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

2 ° approccio

Inizializzazione Startsu un valore predefinito nel costruttore EntityClass. Ciò è utile come se per qualche motivo il valore di Startnon sia impostato prima di salvare l'entità nell'avvio del database avrà sempre un valore predefinito. Assicurarsi che il valore predefinito sia maggiore o uguale a SqlDateTime.MinValue (dal 1 gennaio 1753 al 31 dicembre 9999)

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

3o approccio

Rendi Startdi tipo nullable DateTime -note ? after DateTime-

public virtual DateTime? Start { get; set; }

Per ulteriori spiegazioni leggi questo post


8

Se le DateTimeproprietà sono nullable nel database, assicurarsi di utilizzare DateTime?per le proprietà dell'oggetto associato o EF passerà DateTime.MinValueper valori non assegnati che sono al di fuori dell'intervallo di ciò che il tipo di datetime SQL può gestire.


8

La mia soluzione era quella di passare tutte le colonne datetime a datetime2 e utilizzare datetime2 per tutte le nuove colonne. In altre parole, fai in modo che EF usi datetime2 per impostazione predefinita. Aggiungi questo al metodo OnModelCreating nel tuo contesto:

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

Che otterrà tutto il DateTime e DateTime? proprietà su tutte le entità.


3

inizializza la proprietà Start nel costruttore

Start = DateTime.Now;

Questo ha funzionato per me quando stavo cercando di aggiungere alcuni nuovi campi alla tabella Users (AspNetUsers) di ASP .Net Identity Framework usando Code First. Ho aggiornato la classe - ApplicationUser in IdentityModels.cs e ho aggiunto un campo lastLogin di 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; }
    }

2

Basato sulla risposta dell'utente @ andygjp, è meglio se si sovrascrive il Db.SaveChanges()metodo di base e si aggiunge una funzione per sovrascrivere qualsiasi data che non rientri tra SqlDateTime.MinValue e SqlDateTime.MaxValue.

Ecco il codice di esempio

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

Tratto dal commento dell'utente @ sky-dev su https://stackoverflow.com/a/11297294/9158120


1

Ho avuto lo stesso problema e nel mio caso stavo impostando la data su new DateTime () anziché DateTime.Now


0

Nel mio caso questo è accaduto quando ho usato entità e la tabella sql ha il valore predefinito di datetime == getdate (). quindi cosa ho fatto per impostare un valore in questo campo.


0

Sto usando Database First e quando questo errore mi è successo la mia soluzione è stata quella di forzare ProviderManifestToken = "2005" nel file edmx (rendendo i modelli compatibili con SQL Server 2005). Non so se qualcosa di simile è possibile per Code First.


0

Una riga risolve questo:

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

Quindi, nel mio codice, ho aggiunto:

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

L'aggiunta di una riga alla sottoclasse DBContext sostituisce la sezione void OnModelCreating dovrebbe funzionare.


0

Nel mio caso, dopo alcuni refactoring in EF6, i miei test fallivano con lo stesso messaggio di errore del poster originale, ma la mia soluzione non aveva nulla a che fare con i campi DateTime.

Mi mancava solo un campo obbligatorio durante la creazione dell'entità. Una volta aggiunto il campo mancante, l'errore è scomparso. La mia entità ha due DateTime? campi ma non erano il problema.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.