Как исправить ошибку преобразования datetime2 вне диапазона с помощью DbContext и SetInitializer?


Я использую DbContext и Code First API, представленные в Entity Framework 4.1.

The модель данных использует базовые типы данных, такие как string и DateTime. Единственная аннотация данных, которую я использую в некоторых случаях, - это [Required], но это не на каких-либо DateTime свойства. Пример:

public virtual DateTime Start { get; set; }

The подкласс DbContext также просто и выглядит так:

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

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

The инициализатор устанавливает даты в модель для разумных значений либо в этом году, либо в следующем году.

однако, когда я запускаю инициализатор, я получаю эту ошибку в context.SaveChanges():

преобразование данных datetime2 тип в тип данных datetime привело в значении вне диапазона. Этот заявление было прекращено.

Я не понимаю, почему это происходит вовсе не потому, что все так просто. Я также не уверен, как это исправить, так как нет файла edmx редактировать.

какие идеи?

11 116

11 ответов:

вы должны убедиться, что Start больше или равен SqlDateTime.MinValue (1 января 1753) - по умолчанию Start равен DateTime.MinValue (1 Января 0001).

простой. На свой код во-первых, установить тип datetime в тип datetime?. Так что вы можете работать с типом nullable datetime в базе данных. Пример сущности:

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

В некоторых случаях DateTime.MinValue (или эквивалентно,default(DateTime)) используется для указания неизвестного значения. Этот простой метод расширения должен помочь решить проблему:

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

использование:

 DateTime? dateToPassOnToDb = tooEarlyDate.ToNullIfTooEarlyForDb();

вы можете сделать поле nullable, если это соответствует вашим конкретным задачам моделирования. Нулевая дата не будет принуждаться к дате, которая не находится в диапазоне типа SQL DateTime, как значение по умолчанию. Другой вариант-явно сопоставить с другим типом, возможно, с помощью,

.HasColumnType("datetime2")

хотя этот вопрос довольно старый и уже есть отличные ответы, я подумал, что должен поставить еще один, который объясняет 3 разных подхода к решению этой проблемы.

1-й подход

явного карте DateTime свойства public virtual DateTime Start { get; set; } до datetime2 в соответствующем столбце таблицы. Потому что по умолчанию EF будет сопоставлять его с datetime.

это может быть сделано с помощью fluent API или данных аннотация.

  1. Fluent API

    в классе DbContext overide OnModelCreating и настройка собственность Start (по причинам объяснения это свойство класса 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. аннотации

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

2-й подход

инициализации Start к значению по умолчанию в конструкторе EntityClass.Это хорошо, как будто по какой-то причине значение Start не ставили перед сохранением сущности в базе данных всегда будет иметь значение по умолчанию. Убедитесь, что значение по умолчанию-больше или равно SqlDateTime.MinValue (с 1 января 1753 года по 31 декабря 9999 года)

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

3-й подход

сделать Start иметь тип nullable DateTimeПримечание? после DateTime -

public virtual DateTime? Start { get; set; }

для более подробного объяснения прочитайте это post

мое решение состояло в том, чтобы переключить все столбцы datetime на datetime2 и использовать datetime2 для любых новых столбцов. Другими словами, сделать EF использовать datetime2 по умолчанию. Добавьте это в метод OnModelCreating в вашем контексте:

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

что получит все DateTime и DateTime? свойства для всех ваших объектов.

Если DateTime свойства nullable в базе данных, то не забудьте использовать DateTime? для связанных свойств объекта или EF будет проходить в DateTime.MinValue для неназначенных значений, которые находятся вне диапазона того, что может обрабатывать тип SQL datetime.

инициализировать свойство Start в конструкторе

Start = DateTime.Now;

это сработало для меня, когда я пытался добавить несколько новых полей в таблицу пользователей ASP .Net Identity Framework (AspNetUsers), используя сначала код. Я обновил класс-ApplicationUser в IdentityModels.cs и я добавили поле lastLogin типа 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; }
    }

У меня была та же проблема, и в моем случае я устанавливал дату на новый DateTime() вместо DateTime.Сейчас

в моем случае это произошло, когда я использовал entity и таблица sql имеет значение по умолчанию datetime == getdate(). Итак, что я сделал, чтобы установить значение для этого поля.

сначала я использую базу данных, и когда эта ошибка произошла со мной, мое решение состояло в том, чтобы заставить ProviderManifestToken="2005" в файле edmx (что делает модели совместимыми с SQL Server 2005). Не знаю, возможно ли что-то подобное для кода в первую очередь.