将datetime2数据类型转换为datetime数据类型会导致超出范围的值

455
我有一个包含5列的datatable,在将一行填充数据后通过事务保存到数据库时出现了错误。在保存时,返回了一个错误:

将datetime2数据类型转换为datetime数据类型导致超出范围的值

这意味着,根据所读取的内容,我的datatable具有DateTime2类型,而我的数据库具有DateTime类型;这是错误的。
日期列被设置为 DateTime,代码如下: new DataColumn("myDate", Type.GetType("System.DateTime")) 问题:
这个问题可以在代码中解决还是需要在数据库层面上进行更改?
27个回答

844

简短回答

如果您没有对 DateTime 字段初始化值,则可能会发生此情况;该字段不接受 NULL 值,且它是一个值类型,因此将使用非可空 DateTime 类型的默认值。

对其设置值即可解决问题!

详细回答

default(DateTime) 的值为 DateTime.MinValue(或 new DateTime(1, 1, 1) 或 01/01/0001),这不是有效的 SQL datetime 值。

由于 SQL Server 使用公历,因此 SQL Server datetime 的最低有效值为 01/01/1753。但是,SQL Server DateTime2 支持从 01/01/0001 开始的日期。Entity Framework 默认使用 DateTime2 表示日期,因此生成的 SQL 隐式地将生成的 DateTime2 值强制转换为 SQL Server 上的 DateTime 值。


48
在Entity Framework中,如果您添加了一个非空的Created列,并更新了EDMX,当您在代码中没有设置该值时,可能会以这种方式引发错误。 - Bradley Thomas
6
你是如何解决这个问题的?当你有一个默认值为getdate()调用的日期时间字段时,就会出现这个错误。 - user3046061
17
为什么 Entity Framework 不能忽略 NULL 呢?因为在我的 SQL 代码中,我已经将默认值设为 getdate() 了。 - JoshYates1980
48
我认为的情况是,EntityFramework看到了一个DateTime.MinValue,它是0001年的时间,在SQL中datetime是超出范围的值,因此它发送这个值作为一个DateTime2(支持0001年)的值,所以插入/更新是有效的,但是当SQL尝试将这个DateTime2转换为DateTime时会失败,因为这将导致一个不同的值。两个解决方案是:1 在您的模型中使用可空的datetime;2.在保存上下文更改之前,将所有的datetime值初始化为正确的值。你做出的选择取决于datetime在你的模型中的含义。 - Guillermo Ruffino
2
@GuillermoRuffino的解决方案对我有用。检查所有字段,找到那些0001年的条目。 - Francesco B.
显示剩余9条评论

193

在.NET中,DATETIMEDATETIME2都映射到System.DateTime - 你不能真正进行“转换”,因为它们实际上是相同的.NET类型。

请参阅MSDN文档页面:http://msdn.microsoft.com/en-us/library/bb675168.aspx

这两者的"SqlDbType"有两个不同的值-你可以在DataColumn定义中指定吗?

但是:在SQL Server上,支持的日期范围非常不同。

DATETIME支持1753/1/1到“永恒”(9999/12/31),而DATETIME2支持0001/1/1到永恒。

因此,你真正需要做的是检查日期的年份 - 如果在1753年之前,则需要将其更改为1753之后的日期,以便SQL Server中的DATETIME列可以处理它。

Marc


10
这解释了我遇到的问题。虽然情况很少,需要处理早于1753/1/1的真实日期,但许多情况下会得到默认值0001/1/1,这可能导致错误。 - Hong
我确认当我尝试将 'new DateTime()' 插入到 'datetime' 数据类型时,我收到了这个异常。 - George Onofrei
1
我试图在写入数据库的C#代码中分配DateTime.MinValue的默认值。这解释了我遇到的错误。+1 - Mkalafut
我使用Entity Framework Code First并使用带有DateTime属性的模型。DtInit = new System.DateTime(1492, 10, 12)失败了。 - Kiquenet
这是那种情况之一,真正的原因隐藏在补丁背后... +1 - Eugenio Miró
请更新您的答案,将新的SQL Server "Date"类型包括在内。 - joedotnot

41
在我的SQL Server 2008数据库中,我有一个标记为非空的DateTime列,并且其默认值为GetDate()函数。但是在使用EF4插入新对象时,由于没有明确传递一个DateTime属性,我遇到了这个错误。我本以为SQL函数会自动处理日期,但实际上并没有。我的解决方案是从代码中发送日期值而不是依赖数据库来生成它。
obj.DateProperty = DateTime.now; // C#

2
很高兴能够帮忙。令人烦恼的是,你会认为EF数据上下文在从表格创建对象时应该能够发现字段具有默认值。 - Graham
2
我两周前在VS Live上看到了EF Code First的演示,它看起来非常棒。顺便说一句。 - Graham
1
你好,一年前的评论!我自己正在开始使用EF Code-First,并发现在我的POCO中,我只需要将我的datetime成员定义为Nullable<DateTime>,并且在代码中我可以将其保留为空(而不是01/01/0000)。令人惊喜的是,我发现EF到SQL知道忽略此插入中的null,并使用服务器上的日期(GetDate())...对我们来说,这甚至更可取,因为我们需要更好的服务器一致性,而不必担心Web服务器和SQL服务器之间的时钟差异。 - Funka
虽然我来晚了,但在一份工作中使用了Dapper和POCOS,在另一份工作中使用了EF之后,我不得不说,如果您有使用Dapper而不是EF的选项,那么这非常值得。 - Adrian Carr
我也可以确认其他人关于Dapper和EF的说法。我也更喜欢Dapper。 - Graham
显示剩余3条评论

39

对于我来说,这是因为日期时间是...

01/01/0001 00:00:00

在这种情况下,您想将null分配给EF DateTime对象...以我的FirstYearRegistered代码为例。

DateTime FirstYearRegistered = Convert.ToDateTime(Collection["FirstYearRegistered"]);
if (FirstYearRegistered != DateTime.MinValue)
{
    vehicleData.DateFirstReg = FirstYearRegistered;
}  

我正在使用ExcelDataReader解析这些数据,当输入无效文本时,它会返回01/01/0001(并不像预期的那样抛出异常——使用.GetDateTime(columnIndex)方法)。将其与MinValue进行比较可以防止在SQL中出现超出范围的异常。 - Tommy

26

这个问题让我很烦恼。我想避免使用可空的日期时间 (DateTime?)。我也不能使用 SQL Server 2008 的 datetime2 类型。

modelBuilder.Entity<MyEntity>().Property(e => e.MyDateColumn).HasColumnType("datetime2");

最终我选择了以下内容:

public class MyDb : DbContext
{
    public override int SaveChanges()
    {
        UpdateDates();
        return base.SaveChanges();
    }

    private void UpdateDates()
    {
        foreach (var change in ChangeTracker.Entries<MyEntityBaseClass>())
        {
            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;
                    }
                }
            }
        }
    }
}

1
使用 [Column(TypeName = "datetime2")] - Kiquenet

23
有时候EF并不知道它正在处理一个计算列或一个触发器。按照设计,那些操作将在插入后在EF之外设置一个值。
解决方法是在EF的edmx中为该列指定Computed,并将其放置于StoreGeneratedPattern属性中。 对我而言,当该列有一个插入当前日期和时间的触发器时,就会出现这种情况,详见下面的第三部分。

解决方法

在 Visual Studio 中打开 Model Browser 页面,然后选择 Model,再选择 Entity Types -> 然后

  1. 选择实体和日期时间属性
  2. 选择 StoreGeneratedPattern
  3. 设置为 Computed

EF Model Browser Model Entity Type dialog


在这种情况下,其他答案都是解决方法,因为列的目的是在记录创建时指定时间/日期,而这是SQL执行触发器添加正确时间的工作。例如,这个SQL触发器:DEFAULT (GETDATE()) FOR [DateCreated]

请注意,我在当时确实使用了 GETDATE()。但最近有人评论说,任何 DateTime2 操作都应该使用 SYSDATETIME(),我认为这是正确的。 - ΩmegaMan

11

我遇到了这个问题,于是我在我的日期时间属性中添加了以下内容:

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

1
需要使用 System.ComponentModel.DataAnnotations.Schema; - Kiquenet

10

如果我们不向日期时间字段传递日期时间,则会传递默认日期{1/1/0001 12:00:00 AM}。

但是,这个日期与实体框架不兼容,因此会抛出“将datetime2数据类型转换为datetime数据类型时,结果值超出范围”的错误。

如果您没有传递任何日期,请将default DateTime.now设为日期字段的默认值。

movie.DateAdded = System.DateTime.Now

1
我认为将“DateTime.Now”作为默认值传递远非正确之举,而且相当具有误导性。 - Bartosz
这是真正的原因,我认为这是完美的答案。 - Subarata Talukder

8

最简单的方法是将您的数据库更改为使用datetime2而不是datetime。兼容性很好,您将不会遇到错误。

您仍然需要进行大量测试......

错误可能是因为您尝试将日期设置为0年或其他一些值 - 但这完全取决于您有哪些控制权来更改内容。


不要使用DateTime2:https://towardsdatascience.com/datetime2-why-you-should-not-use-it-70e50ae2bab9 - Zachary Scott

4

我发现这篇文章是为了解决以下错误而写的,其他答案已经解释过了。

将datetime2数据类型转换为datetime数据类型时,导致超出范围的值。

使用可空的DateTime对象。
public DateTime? PurchaseDate { get; set; }

如果你正在使用实体框架 在edmx文件中设置可空属性为True

在edmx文件中设置可空属性为 **True**


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接