web-dev-qa-db-fra.com

Comment corriger l'erreur de conversion hors de date/heure2 en utilisant DbContext et SetInitializer?

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

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

public virtual DateTime Start { get; set; }

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

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

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

Initializer définit les dates du modèle sur des valeurs sensibles pour cette année ou l'année suivante.

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

La conversion d'une donnée datetime2 tapez à un type de données datetime résultant dans une valeur hors limites. Le déclaration a été terminée.

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

Des idées?

125
Alex Angas

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

176
andygjp

Simple . Sur votre code d'abord, définissez le type de DateTime sur DateTime? . Ainsi, vous pouvez utiliser un 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; }
    }
20
Ulisses Ottoni

Dans certains cas, DateTime.MinValue (ou son équivalent, default(DateTime)) est utilisé pour indiquer une valeur inconnue. Cette méthode d'extension simple devrait aider à résoudre le problème:

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

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

.HasColumnType("datetime2")
12
Brian Kretzler

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

1ère approche

Mappez explicitement la propriété DateTimepublic virtual DateTime Start { get; set; } à datetime2 dans la colonne correspondante de la table. Parce que par défaut, EF le mappera sur datetime.

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

  1. API Fluent

    Dans la classe DbContext, remplacez OnModelCreating et configurez la propriété Start (pour des raisons d’explication, il s’agit d’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 de 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. Ceci est correct, car si, pour une raison quelconque, la valeur de Start n'est pas définie avant que l'enregistrement de l'entité au début de la base de données ait 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; }
}

3ème approche

Assurez que Start soit de type nullable DateTime-note? après DateTime-

public virtual DateTime? Start { get; set; }

Pour plus d'explications, lisez ceci post

11
Aim

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

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

Cela va obtenir tous les DateTime et DateTime? propriétés sur toutes vos entités.

8
Ogglas

Si vos propriétés DateTime ont la valeur nullable dans la base de données, veillez à utiliser DateTime? pour les propriétés d'objet associées, sinon EF passera DateTime.MinValue pour les valeurs non attribuées situées en dehors de la plage définie par le type de date/heure SQL.

6
Christopher King

initialiser la propriété Start dans le constructeur

Start = DateTime.Now;

Cela fonctionnait pour moi lorsque j'essayais d'ajouter quelques nouveaux champs à la table des utilisateurs (AspNetUsers) de ASP .Net Identity Framework à l'aide de Code First . J'ai mis à jour le Class - ApplicationUser dans IdentityModels.cs et ajouté 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; }
    }
3
Abhinav Bhandawat

J'avais le même problème et dans mon cas, je mettais la date à new DateTime () au lieu de DateTime.Now

1
sam

Selon la réponse de l'utilisateur @ andygjp, il est préférable de remplacer la méthode de base Db.SaveChanges() et d'ajouter une fonction pour remplacer toute date ne se situant 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;
                    }
                }
            }
        }
    }
}

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

0
Hamza Khanzada

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 de cette ligne à la sous-classe DBContext annule la section OnModelCreating void devrait fonctionner.

0
Howie Krauth

Dans mon cas, cela s'est produit lorsque j'ai utilisé entity et que la table SQL a la valeur par défaut de datetime == getdate () .

0
Ahmad Moayad

Dans mon cas, après un certain refactoring dans EF6, mes tests échouaient avec le même message d'erreur que l'affiche d'origine, mais ma solution n'avait aucun rapport 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 est partie. Mon entité a deux DateTime? champs mais ils n'étaient pas le problème.

0
GrayDwarf

J'utilise Database First et quand cette erreur m'est arrivée, ma solution a été 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 d'abord.

0
Sérgio Azevedo