Як виправити помилку конверсії 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 Includes, і для мого поля було встановлено значення NULL. Тож помилка допомогла виправити мій недогляд.
strattonn

Відповіді:


186

Ви повинні переконатися, що Start є рівним або рівним SqlDateTime.MinValue (1 січня 1753 р.) - за замовчуванням Start дорівнює DateTime.MinValue (1 січня 0001 р.).


14
Я залишив деякі мої об’єкти ініціалізатора без встановленої дати, тому це було б дефолтом до DateTime.MinValue.
Алекс Ангас

Я теж це зробив ^. Додано спеціальне поле дати до об’єкту asp.net ідентичності ApplicationUser, то забув ініціалізувати його до чогось, що мало сенс. : (
Майк Девенні

У моєму випадку проблема полягала в мінімальній даті, 01.01.10001 року генерував помилку.
Мачадо

як я можу перевірити 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 00: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();

13

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

.HasColumnType("datetime2")

12

Незважаючи на те, що це питання досить давнє і вже є чудові відповіді, я подумав, що я повинен поставити ще один, який пояснює 3 різні підходи до вирішення цієї проблеми.

1-й підхід

Явна карта DateTimeвласності public virtual DateTime Start { get; set; }на datetime2у відповідній графі в таблиці. Тому що за замовчуванням EF відобразить його на datetime.

Це можна зробити за допомогою вільного API або анотації даних.

  1. Вільний 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"));
    }
  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; }

Для більш детального пояснення читайте цей пост


8

Якщо DateTimeвластивості обнуляє в базі даних , то обов'язково використовувати DateTime?для відповідних властивостей об'єкта або EF буде проходити в DateTime.MinValueпротягом НЕ привласнених значень, що виходять за межами діапазону , що SQL дати і час типу може обробляти.


8

Моє рішення полягало в тому, щоб переключити всі стовпці з датою на дату2 і використовувати дату2 для будь-яких нових стовпців. Іншими словами, за замовчуванням змушує EF використовувати datetime2. Додайте це до методу OnModelCreating у вашому контексті:

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

Це отримає всі DateTime та DateTime? властивості для всіх ваших організацій.


3

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

Start = DateTime.Now;

Це спрацювало для мене, коли я намагався додати кілька нових полів до таблиці користувачів ASP .Net Identity Framework (AspNetUsers), використовуючи 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

У моєму випадку це сталося, коли я використовував сутність і таблиця 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 замінює недійсний розділ OnModelCreating повинен працювати.


0

У моєму випадку, після деякого рефакторингу в EF6, мої тести провалювалися з тим же повідомленням про помилку, що і оригінальний плакат, але моє рішення не мало нічого спільного з полями DateTime.

Мені просто не вистачало обов'язкового поля при створенні сутності. Щойно я додав поле, яке не було, помилка зникла. У моєї організації є два DateTime? поля, але вони не були проблемою.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.