Подтвердить что ты не робот

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

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

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

public virtual DateTime Start { get; set; }

Подкласс DbContext также прост и выглядит следующим образом:

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

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

Инициализатор устанавливает даты в модели на разумные значения в этом году или в следующем году.

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

Преобразование данных datetime2 тип к типу данных даты и времени в значении вне диапазона. утверждение завершено.

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

Любые идеи?

4b9b3361

Ответ 1

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

Ответ 2

Simple. В коде сначала укажите тип DateTime для DateTime?. Таким образом, вы можете работать с нулевым типом 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; }
    }

Ответ 3

В некоторых случаях 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();

Ответ 4

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

.HasColumnType("datetime2")

Ответ 5

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

1-й подход

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

Это можно сделать с помощью свободного API или аннотации данных.

  • Свободный API

    В классе DbContext переопределить 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"));
    }
    
  • Аннотации данных

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

Второй подход

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

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

Третий подход

Сделать Start типом nullable DateTime -note ? после DateTime -

public virtual DateTime? Start { get; set; }

Подробнее об этом читайте post

Ответ 6

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

Ответ 7

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

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

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

Ответ 8

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

Start = DateTime.Now;

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

Ответ 9

Исходя из ответа пользователя @andygjp, лучше переопределить базовый Db.SaveChanges() и добавить функцию для переопределения любой даты, которая не попадает между SqlDateTime.MinValue и SqlDateTime.MaxValue.

Вот пример кода

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

Взято у пользователя @sky-dev комментарий на fooobar.com/questions/42108/...

Ответ 10

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

Ответ 11

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

Ответ 12

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

Ответ 13

Одна строка исправляет это:

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

Итак, в своем коде я добавил:

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

Добавление этой строки в переопределение подкласса DBContext void OnModelCreating должно работать.

Ответ 14

В моем случае после некоторого рефакторинга в EF6 мои тесты не прошли с тем же сообщением об ошибке, что и у исходного постера, но мое решение не имело ничего общего с полями DateTime.

Я просто пропустил обязательное поле при создании объекта. Как только я добавил недостающее поле, ошибка исчезла. У моей сущности есть два DateTime? поля, но они не были проблемой.