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


136

我正在使用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数据类型到datetime数据类型的转换导致值超出范围。该语句已终止。

我根本不明白为什么会这样,因为一切都很简单。我也不确定如何解决它,因为没有edmx文件可以编辑。

有任何想法吗?


2
您可以使用SQL事件探查器查看插入/更新SQL语句吗?很难说这是怎么回事-我们看不到您的初始化程序或实体。SQL Profiler将为您提供很多本地化问题的帮助。
Ladislav Mrnka

1
就我而言,我已经在表和编辑表单中添加了一个字段,却忘记了更新“绑定包含”,并且我的字段被设置为NULL。因此,该错误有助于纠正我的疏忽。
strattonn 2014年

Answers:


186

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


14
我已经将一些初始值设定项对象设置为未设置日期,因此它将默认为DateTime.MinValue。
亚历克斯·安加斯

我也这样做了^。在asp.net身份ApplicationUser对象中添加了一个自定义日期字段,然后忘记将其初始化为有意义的东西。::(
Mike Devenney

就我而言,问题出在最小日期,01/01/0001产生了错误。
马查多

我如何检查dateTime.minvalue?
Anabeil '18

1
有点晚了,但是@Anabeil您应该能够在VS / linqpad的直接窗口中只是Console.WriteLine(DateTime.MinValue)
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();

13

如果适合您的特定建模问题,则可以使该字段为空。空日期不会被强制转换为不在SQL DateTime类型范围内的日期,而该日期将是默认值。另一种选择是显式映射到其他类型,也许使用

.HasColumnType("datetime2")

12

即使这个问题已经很老了,并且已经有了很好的答案,但我认为我应该再提出一个解释3种不同方法来解决此问题的方法。

第一种方法

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

第二种方法

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

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

第三种方法

Start为类型可空的DateTime -音符 ?DateTime-

public virtual DateTime? Start { get; set; }

有关更多说明,请阅读此帖子


8

如果您的DateTime属性在数据库中可以为空,则请确保将其DateTime?用于关联的对象属性,否则EF将传递DateTime.MinValue未分配值(超出SQL datetime类型可以处理的范围)。


8

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

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

那会得到所有的DateTime和DateTime吗?您所有实体上的属性。


3

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

Start = DateTime.Now;

当我尝试使用Code First向ASP .Net Identity Framework的Users表(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; }
    }

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

我正在使用数据库优先,当我遇到此错误时,我的解决方案是在edmx文件中强制ProviderManifestToken =“ 2005”(使模型与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吗?字段,但这不是问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.