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

136

Я использую 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 в тип данных datetime привело к значению вне допустимого диапазона. Заявление было прекращено.

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

Любые идеи?

Алекс Ангас
источник
2
Можете ли вы использовать SQL Profiler для просмотра вставки / обновления операторов SQL? Трудно сказать, что здесь происходит - мы не видим ваш инициализатор или сущности. SQL Profiler поможет вам во многом локализовать проблему.
Ладислав Мрнка
1
В моем случае я добавил поле в таблицу и форму редактирования, забыл обновить Bind Include, и мое поле было установлено в NULL. Так что ошибка помогла исправить мой недосмотр.
Страттонн

Ответы:

186

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

andygjp
источник
14
Я оставил некоторые из своих объектов инициализатора без установленной даты, поэтому по умолчанию было бы DateTime.MinValue.
Алекс Ангас
Я тоже это сделал. Добавил настраиваемое поле даты в объект ApplicationUser удостоверения asp.net, а затем забыл инициализировать его чем-то, что имело смысл. : (
Майк Девенни
В моем случае проблема была в минимальной дате, 01.010001 генерировала ошибку.
Мачадо
Как я могу проверить dateTime.minvalue?
Анабейль
1
Немного поздно, но @Anabeil вы сможете просто Console.WriteLine (DateTime.MinValue) в ближайшем окне VS / linqpad
SIRHAMY
22

Просто. Сначала в своем коде установите тип 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; }
    }
Улисс Оттони
источник
6
это не всегда так. {1/1/0001 12:00:00 AM} также приводит к появлению этой ошибки
eran otzap
20

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

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

.HasColumnType("datetime2")
Брайан Кретцлер
источник
12

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

1-й подход

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

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

  1. Свободный 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чтобы быть типа обнуляемым- DateTime примечание ? после DateTime-

public virtual DateTime? Start { get; set; }

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

цель
источник
8

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

Кристофер Кинг
источник
8

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

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

Что получит все DateTime и DateTime? свойства на всех ваших лиц.

Ogglas
источник
3

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

Start = DateTime.Now;

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

Исходя из ответа пользователя @ 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 на https://stackoverflow.com/a/11297294/9158120

Хамза Ханзада
источник
1

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

Сэм
источник
0

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

Ахмад Моаяд
источник
0

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

Сержиу Азеведо
источник
0

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

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 должно работать.

Хоуи Краут
источник
0

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

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

GrayDwarf
источник