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

我使用的是实体框架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 文件编辑。

有什么想法吗?

189686 次浏览

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

如果符合您特定的建模关注点,您可以使字段为空。空日期不会像默认值那样被强制为不在 SQLDateTime 类型范围内的日期。另一种选择是显式地映射到不同的类型,

.HasColumnType("datetime2")

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

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

Start = DateTime.Now;

当我试图向 ASP 添加一些新字段时,这对我很有用。Net Identity Framework 的用户表(AspNetUsers)使用代码优先。 我更新了 Class-ApplicationUser 的 identitymodels.cs ,并添加了类型为 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; }
}

尽管这个问题已经很老了,而且已经有了很好的答案,我还是觉得我应该再加上一个,解释解决这个问题的3种不同方法。

第一次进场

显式地将 DateTime属性 public virtual DateTime Start { get; set; }映射到表中相应列中的 datetime2。因为默认情况下 EF 将它映射到 datetime

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

  1. 流利的空气污染指数

    在 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. Data annotation

    [Column(TypeName="datetime2")]
    public virtual DateTime Start { get; set; }
    

2nd Approach

Initialize Start to a default value in EntityClass constructor.This is good as if for some reason the value of Start is not set before saving the entity into the database start will always have a default value. Make sure default value is greater than or equal to SqlDateTime.MinValue ( from January 1, 1753 to December 31, 9999)

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

第三次进场

DateTime-之后,使 Start成为类型可空的 DateTime 笔记 ?

public virtual DateTime? Start { get; set; }

更多解释请阅读此 邮寄

如果您的 DateTime属性在数据库中是可以为空的,那么一定要使用 DateTime?作为相关的对象属性,否则 EF 将传递 DateTime.MinValue作为未分配的值,这超出了 SQL 日期时间类型可以处理的范围。

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

在我的例子中,当我使用实体并且 sql 表具有缺省值 datetime = = getdate ()时,就会发生这种情况。 所以我设置了这个字段的值。

我遇到了同样的问题,在我的例子中,我将日期设置为 new DateTime ()而不是 DateTime。现在

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

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

这将获取所有实体上的所有 DateTime 和 DateTime? 属性。

我正在使用 Database First,当这个错误发生在我身上时,我的解决方案是在 edmx 文件中强制 ProviderManifestToken = “2005”(使模型与 SQL Server 2005兼容)。 不知道代码优先是否有类似的可能性。

根据 user@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的评论

有一句话解决了这个问题:

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

因此,在我的代码中,我添加了:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));
}

将这一行添加到 DBContext 子类覆盖 void OnModelCreate 部分应该可以工作。

在我的例子中,在 EF6中进行了一些重构之后,我的测试失败了,出现了与原始海报相同的错误消息,但是我的解决方案与 DateTime 字段没有任何关系。

我只是在创建实体时遗漏了一个必填字段。一旦我添加了缺少的字段,错误就消失了。我的实体确实有两个日期时间?但他们不是问题所在。

万一有人跟我一样蠢,再查查你约会的年份。我将日期从 YYMMDD 格式的文本文件转换为0020年的日期,而不是2020年的日期。明显的错误,但我花了更多的时间看它,但没有看到它比我应该有!