Как исправить ошибку преобразования datetime2 вне диапазона, используя DbContext и SetInitializer? - PullRequest
126 голосов
/ 19 мая 2011

Я использую 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 для редактирования.

Есть идеи?

Ответы [ 13 ]

179 голосов
/ 19 мая 2011

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

21 голосов
/ 11 мая 2015

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; }
    }
19 голосов
/ 25 октября 2013

В некоторых случаях 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();
12 голосов
/ 19 мая 2011

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

.HasColumnType("datetime2")
11 голосов
/ 21 января 2015

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

1-й подход

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

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

  1. Fluent API

    InПереопределение класса 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 -note ? после DateTime -

public virtual DateTime? Start { get; set; }

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

8 голосов
/ 29 августа 2016

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

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

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

7 голосов
/ 27 января 2015

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

3 голосов
/ 09 августа 2014

инициализировать свойство 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; }
    }
1 голос
/ 18 февраля 2019

На основании ответа пользователя @ 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 голос
/ 25 марта 2016

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

...