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

196

我在HomeController中有以下代码:

public ActionResult Edit(int id)
{
    var ArticleToEdit = (from m in _db.ArticleSet where m.storyId == id select m).First();
    return View(ArticleToEdit);
}

[ValidateInput(false)]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Article ArticleToEdit)
{
    var originalArticle = (from m in _db.ArticleSet where m.storyId == ArticleToEdit.storyId select m).First();
    if (!ModelState.IsValid)
        return View(originalArticle);

    _db.ApplyPropertyChanges(originalArticle.EntityKey.EntitySetName, ArticleToEdit);
    _db.SaveChanges();
    return RedirectToAction("Index");
}

这是Edit方法的视图:

<% using (Html.BeginForm()) {%>

    <fieldset>
        <legend>Fields</legend>
        <p>
            <label for="headline">Headline</label>
            <%= Html.TextBox("headline") %>
        </p>
        <p>
            <label for="story">Story <span>( HTML Allowed )</span></label>
            <%= Html.TextArea("story") %>
        </p>
        <p>
            <label for="image">Image URL</label>
            <%= Html.TextBox("image") %>
        </p>
        <p>
            <input type="submit" value="Post" />
        </p>
    </fieldset>

<% } %>

当我点击提交按钮时,出现错误:{"将datetime2数据类型转换为datetime数据类型导致值超出范围。\r\n语句已终止。"} 有什么想法吗? 我假设编辑方法正在尝试将数据库中的发布值更新为编辑值,但由于某种原因它不喜欢它...虽然我不明白为什么日期涉及其中,因为在编辑控制器方法中没有提到。


1
modelBuilder.Entity<WorldInfo>().Property(d => d.CurrentTime).HasColumnType("datetime2"); - Anthony Nichols
24个回答

181
问题在于你正在使用 ApplyPropertyChanges 来处理一个模型对象,该模型对象只被表单中的数据填充了部分字段(标题、故事和图像)。ApplyPropertyChanges 会应用到对象的所有属性,包括未初始化的 DateTime,它的默认值为0001-01-01,这个值超出了 SQL Server 的 DATETIME 范围。
建议改用先检索要修改的对象,再更改表单编辑的具体字段,最后保存带有这些修改的对象。这样,仅会修改更改过的字段。或者,你可以在页面中使用隐藏输入来填充其他字段,但这对并发编辑不够友好。
以下是一个未经测试的示例,演示如何仅更新对象的某些字段(假设你正在使用 LINQ to SQL):
var story = _db.ArticleSet.First(a => a.storyId == ArticleToEdit.storyId);
story.headline = ArticleToEdit.headline;
story.story = ArticleToEdit.story;
story.image = ArticleToEdit.image;
story.modifiedDate = DateTime.Now;
_db.SubmitChanges();

SubmitChanges在我的应用程序中不起作用:/ 是否可以将story.modifiedDate = DateTime.Now;添加到我的当前代码中?谢谢 - Cameron
搞定了 :) ArticleToEdit.posted = DateTime.Now; 感谢你的所有帮助。 - Cameron
刚刚遇到了同样的问题,使用Code First,将我的datetime字段设置为可空。感谢提示。 - JTew
@Jacob:我已经使用了IsModified并将其设置为false,修改日期的写法与您提到的相同。 我有另一个类,在我的编辑逻辑中也起作用。但是在其中一个类中不起作用。我已经检查了数据库和属性CreatedOnModifiedOn的数据类型为datetime。有什么建议可以解决错误吗? - Vini
1
当我们设置public DateTime CreatedDate而不是public DateTime? CreatedDate时,就会出现这个错误,因为DateTime不能为null,所以它为什么会给出超出范围的错误。这可能有帮助,但它解决了我的问题。 - adnan
显示剩余5条评论

130
这是使用Entity Framework时人们经常遇到的常见错误。当与保存的表相关联的实体具有必填的日期时间字段且您没有设置它的某个值时,就会出现这种情况。
默认的datetime对象创建时会赋一个值为01/01/1000,并将其用于代替null。这将被发送到可以容纳从1753-01-01 00:00:00及以后日期值的datetime列,但不能在此之前,从而导致超出范围的异常。
可以通过修改数据库字段以接受null或初始化该字段的值来解决此错误。

6
当数据库中的日期时间字段是可选的且没有设置值时,就会出现这种情况。因此,这不是普通用户的错误,而是EF架构问题之一。 - GSoft Consulting
1
我同意EF应该查看数据库中的类型并相应地进行调整,或者至少抛出一个更精确的错误。另一方面,我喜欢你的回答,因为你提供了2个选项而不是通常的一个 - 用最小值初始化字段。 - DanteTheSmith
这解决了我的问题。具体来说,我试图传递仅年份而不是有效的日期时间(例如:01-05-2021)。 - David Tunnell

47

DATETIME 支持从1753年1月1日到“永远”(9999年12月31日),而DATETIME2 支持从0001年1月1日至永远。

Msdn

Answer: 我猜测你尝试将DateTime的值设置为'0001/1/1'。如果是这样,请设置断点并进行调试,然后用null替换DateTime或者设置正常的日期。


但是我应该在控制器中放什么呢?调试只会弹出我上面发布的错误。谢谢。 - Cameron
这里似乎有错误 -> _db.SaveChanges(); 在这行代码之前你可以设置断点... - Andrew Orsich
在调用_db.SaveChanges()之前,你是否检查了DateTime的值?它应该大于'1753/1/1'。 - Andrew Orsich
正如@Jacob所说,ApplyPropertyChanges会将更改应用于对象的所有属性,并尝试将{01/01/0001 00:00:00}保存到数据库中(这会导致错误)。因此,请使用@Jacob发布的示例。 - Andrew Orsich
当我们设置public DateTime CreatedDate而不是public DateTime? CreatedDate时,会出现此错误,因为DateTime不能为null,所以为什么它会给出超出范围的错误。这可能有帮助,但它解决了我的问题。 - adnan
显示剩余2条评论

17

这个问题让我疯了。我想避免使用可空日期时间 (DateTime?)。我也不能使用 SQL 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;
                    }
                }
            }
        }
    }
}

12

如果您的模型为 Entity Framework 版本 >= 5,您也可以通过添加以下内容来解决此问题。

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreationDate { get; set; }

8
如果您有一个允许为空的日期时间列,则会出现此错误。建议在.SaveChanges()之前设置要传递给对象的值。

5

在我更改了我的模型(Code First)后,出现了以下错误:

public DateTime? DateCreated

to

public DateTime DateCreated

在DateCreated字段中出现空值会导致此错误。因此,我不得不手动使用SQL UPDATE语句来将该字段初始化为标准值。

另一种解决方案可能是指定该字段的默认值。


3
【已解决】在Entity Framework Code First中(我的情况),只需将DateTime更改为DateTime?即可解决我的问题。
/*from*/ public DateTime SubmitDate { get; set; }
/*to  */ public DateTime? SubmitDate { get; set; }

这与上面的Badr Bellaj相同,但是能够将解决方案缩减到一个字符真是太棒了。如果您没有使用代码优先,它也可以工作,但在这种情况下,值得检查您的数据库列是否允许空值。 - andrew pate

3

看起来您正在使用实体框架。我的解决方案是将所有的日期时间列切换为datetime2,并对任何新列使用datetime2,换句话说,使EF默认使用datetime2。在您的上下文中添加以下内容到OnModelCreating方法:

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

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


3
在我的情况下,在我使用的类的初始化器中,我没有为我的DateTime属性设置任何默认值,因此导致@Andrew Orsich'答案中解释的问题。因此,我只需将该属性设置为可空。或者我也可以在构造函数中给它赋值DateTime.Now。希望对某些人有所帮助。

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