Comment corriger l'erreur de conversion hors plage datetime2 à l'aide de DbContext et SetInitializer?

136

J'utilise les API DbContext et Code First introduites avec Entity Framework 4.1.

Le modèle de données utilise des types de données de base tels que stringet DateTime. La seule annotation de données que j'utilise dans certains cas est [Required], mais ce n'est sur aucune des DateTimepropriétés. Exemple:

public virtual DateTime Start { get; set; }

La sous - classe DbContext est également simple et ressemble à:

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

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

L' initialiseur définit les dates du modèle sur des valeurs raisonnables pour cette année ou l'année prochaine.

Cependant, lorsque j'exécute l'initialiseur, j'obtiens cette erreur à context.SaveChanges():

La conversion d'un type de données datetime2 en un type de données datetime a abouti à une valeur hors plage. La déclaration a été terminée.

Je ne comprends pas du tout pourquoi cela se produit parce que tout est si simple. Je ne sais pas non plus comment le réparer car il n'y a pas de fichier edmx à modifier.

Des idées?

Alex Angas
la source
2
Pouvez-vous utiliser SQL Profiler pour voir insérer / mettre à jour des instructions SQL? Il est assez difficile de dire ce qui se passe ici - nous ne voyons pas votre initialiseur ou vos entités. SQL Profiler vous aidera beaucoup à localiser le problème.
Ladislav Mrnka
1
Dans mon cas, j'avais ajouté un champ à une table et un formulaire d'édition, oublié de mettre à jour le Bind Comprend et mon champ devenait NULL. L'erreur a donc aidé à corriger mon oubli.
strattonn le

Réponses:

186

Vous devez vous assurer que Start est supérieur ou égal à SqlDateTime.MinValue (1er janvier 1753) - par défaut, Start est égal à DateTime.MinValue (1er janvier 0001).

andygjp
la source
14
J'avais laissé certains de mes objets d'initialisation sans date fixe, donc il aurait été par défaut DateTime.MinValue.
Alex Angas
J'ai fait ça aussi ... Ajout d'un champ de date personnalisé à l'objet ApplicationUser d'identité asp.net, puis j'ai oublié de l'initialiser à quelque chose qui avait du sens. : (
Mike Devenney
Dans mon cas, le problème était dans la date min, 01/01/0001 générait l'erreur.
Machado
comment puis-je vérifier dateTime.minvalue?
Anabeil
1
Un peu tard, mais @Anabeil vous devriez pouvoir simplement Console.WriteLine (DateTime.MinValue) dans la fenêtre immédiate de VS / linqpad
SIRHAMY
22

Facile. Sur votre code d'abord, définissez le type de DateTime sur DateTime?. Vous pouvez donc travailler avec le type DateTime nullable dans la base de données. Exemple d'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; }
    }
Ulisses Ottoni
la source
6
ce n'est pas toujours le cas. {1/1/0001 12:00:00 AM} fait également apparaître cette erreur
eran otzap
20

Dans certains cas, DateTime.MinValue(ou de manière équivalente default(DateTime)) est utilisé pour indiquer une valeur inconnue.

Cette méthode d'extension simple peut aider à gérer de telles situations:

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

Usage:

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

Vous pouvez rendre le champ nullable, si cela répond à vos préoccupations de modélisation spécifiques. Une date nulle ne sera pas forcée à une date qui n'est pas dans la plage du type SQL DateTime comme le ferait une valeur par défaut. Une autre option consiste à mapper explicitement à un type différent, peut-être avec,

.HasColumnType("datetime2")
Brian Kretzler
la source
12

Même si cette question est assez ancienne et qu'il y a déjà d'excellentes réponses, j'ai pensé que je devrais en poser une de plus qui explique 3 approches différentes pour résoudre ce problème.

1ère approche

Carte Explicitement DateTimepropriété public virtual DateTime Start { get; set; }à datetime2la colonne correspondante du tableau. Parce que par défaut, EF le mappera à datetime.

Cela peut être fait par une API fluide ou une annotation de données.

  1. API Fluent

    Dans la classe DbContext OnModelCreating, remplacez et configurez la propriété Start(pour des raisons d'explication, c'est une propriété de la 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. Annotation des données

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

2ème approche

Initialisez Startà une valeur par défaut dans le constructeur EntityClass. C'est bien comme si, pour une raison quelconque, la valeur de Startn'est pas définie avant l'enregistrement de l'entité dans le démarrage de la base de données aura toujours une valeur par défaut. Assurez-vous que la valeur par défaut est supérieure ou égale à SqlDateTime.MinValue (du 1er janvier 1753 au 31 décembre 9999)

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

3e approche

Faire Starten sorte qu'il soit de type DateTime nullable -note ? après DateTime-

public virtual DateTime? Start { get; set; }

Pour plus d'explications, lisez cet article

objectif
la source
8

Si vos DateTimepropriétés peuvent être nulles dans la base de données, assurez-vous de les utiliser DateTime?pour les propriétés d'objet associées, sinon EF passera DateTime.MinValuepour les valeurs non attribuées qui sont en dehors de la plage de ce que le type de date / heure SQL peut gérer.

Christopher King
la source
8

Ma solution était de basculer toutes les colonnes datetime en datetime2 et d'utiliser datetime2 pour toutes les nouvelles colonnes. En d'autres termes, faites en sorte qu'EF utilise datetime2 par défaut. Ajoutez ceci à la méthode OnModelCreating sur votre contexte:

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

Cela obtiendra tous les DateTime et DateTime? properties sur toutes vos entités.

Ogglas
la source
3

initialiser la propriété Start dans le constructeur

Start = DateTime.Now;

Cela a fonctionné pour moi lorsque j'essayais d'ajouter quelques nouveaux champs à la table des utilisateurs de ASP .Net Identity Framework (AspNetUsers) en utilisant Code First. J'ai mis à jour la classe - ApplicationUser dans IdentityModels.cs et j'ai ajouté un champ lastLogin de type 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
la source
2

En fonction de la réponse de user @ andygjp, il est préférable de remplacer la Db.SaveChanges()méthode de base et d'ajouter une fonction pour remplacer toute date qui ne tombe pas entre SqlDateTime.MinValue et SqlDateTime.MaxValue.

Voici l exemple de code

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

Tiré du commentaire de l'utilisateur @ sky-dev sur https://stackoverflow.com/a/11297294/9158120

Hamza Khanzada
la source
1

J'ai eu le même problème et dans mon cas, je définissais la date sur le nouveau DateTime () au lieu de DateTime.

sam
la source
0

Dans mon cas, cela s'est produit lorsque j'ai utilisé entity et que la table sql a la valeur par défaut datetime == getdate (). alors ce que j'ai fait pour définir une valeur dans ce champ.

Ahmad Moayad
la source
0

J'utilise Database First et lorsque cette erreur m'est arrivée, ma solution était de forcer ProviderManifestToken = "2005" dans le fichier edmx (rendant les modèles compatibles avec SQL Server 2005). Je ne sais pas si quelque chose de similaire est possible pour Code First.

Sérgio Azevedo
la source
0

Une ligne corrige ceci:

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

Donc, dans mon code, j'ai ajouté:

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

L'ajout d'une ligne à la section de remplacement de sous-classe DBContext void OnModelCreating devrait fonctionner.

Howie Krauth
la source
0

Dans mon cas, après quelques refactorisations dans EF6, mes tests échouaient avec le même message d'erreur que l'affiche d'origine mais ma solution n'avait rien à voir avec les champs DateTime.

Il me manquait juste un champ obligatoire lors de la création de l'entité. Une fois que j'ai ajouté le champ manquant, l'erreur a disparu. Mon entité a deux DateTime? champs mais ce n'était pas le problème.

GrayDwarf
la source