如何使用DbContext和SetInitializer修复datetime2超出范围的转换错误?

时间:2011-05-18 20:56:21

标签: c# .net entity-framework ef-code-first dbcontext

我正在使用Entity Framework 4.1中引入的DbContext和Code First API。

数据模型使用基本数据类型,例如stringDateTime。我在某些情况下使用的唯一数据注释是[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文件。

有什么想法吗?

15 个答案:

答案 0 :(得分:179)

您必须确保Start大于或等于SqlDateTime.MinValue(1753年1月1日) - 默认情况下,Start等于DateTime.MinValue(0001年1月1日)。

答案 1 :(得分:21)

简单。 首先在代码中,将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; }
    }

答案 2 :(得分:19)

在某些情况下,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();

答案 3 :(得分:12)

如果符合您特定的建模问题,您可以将该字段设为可为空。空日期不会像默认值那样被强制转换为不在SQL DateTime类型范围内的日期。另一种选择是明确映射到不同的类型,可能使用

.HasColumnType("datetime2")

答案 4 :(得分:11)

即使这个问题已经很老了,而且已经有了很好的答案,我想我应该再提一个解释这个问题的3种不同方法。

第一种方法

在表格的相应列中明确地将DateTime属性public virtual DateTime Start { get; set; }映射到datetime2。因为默认情况下,EF会将其映射到datetime

这可以通过流畅的API或数据注释来完成。

  1. Fluent 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; }
    
  3. 第二种方法

    Start初始化为EntityClass构造函数中的默认值。这很好,好像由于某种原因,在将实体保存到数据库之前未设置Start的值,start将始终具有默认值。确保默认值大于或等于SqlDateTime.MinValue(从1753年1月1日到9999年12月31日)

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

    第3种方法

    Start -

    之后,使DateTime类型为可为空? -note DateTime
    public virtual DateTime? Start { get; set; }
    

    有关详细说明,请阅读此post

答案 5 :(得分:8)

我的解决方案是将所有日期时间列切换为datetime2,并将datetime2用于任何新列。换句话说,默认情况下,EF使用datetime2。将其添加到上下文中的OnModelCreating方法:

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

那将获得所有DateTime和DateTime?所有实体的属性。

答案 6 :(得分:7)

如果您的DateTime属性在数据库中可以为空,那么请务必使用DateTime?作为关联的对象属性,否则EF将传入DateTime.MinValue以获取超出范围的未分配值SQL日期时间类型可以处理的内容。

答案 7 :(得分:3)

初始化构造函数中的Start属性

Start = DateTime.Now;

当我尝试使用Code First向ASP .Net Identity Framework的用户表(AspNetUsers)添加一些新字段时,这对我有用。 我在IdentityModels.cs中更新了Class - ApplicationUser,并添加了DateTime类型的字段lastLogin。

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

答案 8 :(得分:1)

我有同样的问题,在我的情况下,我将日期设置为新的DateTime()而不是DateTime.Now

答案 9 :(得分:0)

在我的情况下,这发生在我使用实体时,sql表的默认值为datetime == getdate()。 所以我在这个领域设置了一个值。

答案 10 :(得分:0)

我正在使用Database First,当我发生此错误时,我的解决方案是在edmx文件中强制使用ProviderManifestToken =“2005”(使模型与SQL Server 2005兼容)。 不知道Code First是否有类似的东西。

答案 11 :(得分:0)

基于用户@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的评论

答案 12 :(得分: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部分。

答案 13 :(得分:0)

在我的情况下,在EF6中进行了一些重构后,我的测试失败了,并显示了与原始发布者相同的错误消息,但是我的解决方案与DateTime字段无关。

创建实体时,我只是缺少一个必填字段。一旦我添加了缺少的字段,错误就消失了。我的实体有两个DateTime吗?字段,但这不是问题。

答案 14 :(得分:-1)

如果有人像我一样讨厌,请仔细检查您约会的年份。我正在从YYMMDD格式的文本文件转换日期,因此创建的日期是0020年,而不是2020年。明显的错误,但是我花了更多时间在查看它,但没有看到我应有的时间!