为什么EF5 Code First在将可空的DateTime插入数据库时使用DateTime2?

7
我正在将一个具有可空日期时间的Cart对象保存到数据库中。这是我得到的错误信息:
“将datetime2数据类型转换为datetime数据类型时,导致值超出范围。”
有很多stackoverflow帖子记录了解决此问题的方法。然而,当code first创建数据库时,它将创建字段作为DateTime(允许为空)。但由于某种原因,code first尝试使用DateTime2字段进行插入。
我想知道为什么EF以一种方式创建字段,但对同一字段使用不同类型进行插入。
以下是领域对象:
using System;
using System.Collections.Generic;

namespace Core.Domain.Cart
{
    public partial class Cart : BaseEntity, ILocalizedEntity
    {
        private ICollection<Catalog> _catalogs;

        /// <summary>
        /// Gets or sets the name
        /// </summary>
        public virtual string Name { get; set; }

        /// <summary>
        /// Gets or sets the zone identifier
        /// </summary>
        public virtual int ZoneId { get; set; }

        /// <summary>
        /// Gets or sets the brand identifier
        /// </summary>
        public virtual int BrandId { get; set; }

        /// <summary>
        /// Gets or sets the customer type identifier
        /// </summary>
        public virtual int CustomerTypeId { get; set; }

        /// <summary>
        /// Gets or sets the date and time of the opening of a cart
        /// </summary>
        public virtual DateTime? OpeningDateUtc { get; set; }

        /// <summary>
        /// Gets or sets the date and time of the closing of a cart
        /// </summary>
        public virtual DateTime? ClosingDateUtc { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether the entity is online or not
        /// </summary>
        public virtual bool IsOnline { get; set; }

        /* Truncated for relevance */
    }    
}

这个模型:

using FluentValidation.Attributes;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using Telerik.Web.Mvc;


namespace Admin.Models.Cart
{
        [Validator(typeof(CartValidator))]
        public partial class CartModel : BaseNopEntityModel, ILocalizedModel<CartLocalizedModel>
        {            
            public CartModel()
            {
                Locales = new List<CartLocalizedModel>();
                Catalogs = new List<CatalogModel>();
                UnassociatedCatalogs = new List<CatalogModel>();
            }
            [NopResourceDisplayName("Admin.Carts.Fields.Name")]
            [AllowHtml]
            public string Name { get; set; }

            //Zone dropdown
            [NopResourceDisplayName("Admin.Carts.Fields.ZoneList")]
            public SelectList ZoneList { get; set; }        //The dropdown with zones
            public int ZoneId { get; set; }                 //The selected value of the dropdown once the form is submitted
            public string ZoneName { get; set; }            //The name of the zone to display in data-grid List view.

            //Brand dropdown
            [NopResourceDisplayName("Admin.Carts.Fields.BrandList")]
            public SelectList BrandList { get; set; }       //The dropdown with brands
            public int BrandId { get; set; }                //The selected value of the dropdown once the form is submitted
            public string BrandName { get; set; }           //The name of the brand to display in the data-grid List view. 

            //Customer type dropdown
            [NopResourceDisplayName("Admin.Carts.Fields.CustomerTypeList")]
            public SelectList CustomerTypeList { get; set; }//The dropdown with CustomerType
            public int CustomerTypeId { get; set; }         //The selected value of the dropdown once the form is submitted
            public string CustomerTypeName { get; set; }    //The name of the CustomerType to display in the data-grid List view. 

            [NopResourceDisplayName("Admin.Carts.Fields.OpeningDateUtc")]
            [UIHint("DateNullable")]
            public DateTime? OpeningDateUtc { get; set; }

            [NopResourceDisplayName("Admin.Carts.Fields.ClosingDateUtc")]
            [UIHint("DateNullable")]
            public DateTime? ClosingDateUtc { get; set; }

            [NopResourceDisplayName("Admin.Carts.Fields.IsOnline")]
            public bool IsOnline { get; set; }
            
            /* Truncated for relevance */
        }

}

所以,OpeningDateUtcClosingDateUtc都是DateTime类型吗?
这是通过EF Code First生成数据库的方式: EF generated table OpeningDateUtcClosingDateUtc被创建为可空的DateTime字段。
那么,当我使用IDBContext.SaveChanges()进行保存时,为什么生成的查询SQL是:
exec sp_executesql N'update [dbo].[Cart]
set [Name] = @0, [ZoneId] = @1, [BrandId] = @2, [CustomerTypeId] = @3, [OpeningDateUtc] = @4, [ClosingDateUtc] = @5, [IsOnline] = @6, [IsReadonly] = @7, [IsPreviewMode] = @8, [CreatedOnUtc] = @9
where ([Id] = @10)
',N'@0 nvarchar(100),@1 int,@2 int,@3 int,@4 datetime2(7),@5 datetime2(7),@6 bit,@7 bit,@8 bit,@9 datetime2(7),@10 int',@0=N'Cart1',@1=7,@2=4,@3=5,@4='2013-01-09 00:00:00',@5='2013-01-18 00:00:00',@6=0,@7=0,@8=1,@9='0001-01-01 00:00:00',@10=1

有趣的部分是@4 datetime2(7),@5 datetime2(7)

我知道我可以通过在cart映射中添加.HasColumnType("datetime2")来解决这个问题,但这并不能回答为什么EF5(以及可能更早版本)将它们设置为可空日期时间。

2个回答

18
在.NET中,DateTime类型与SQL Server中的datetime2类型具有相同的范围和精度。当EF在SQL Server中插入或更新datetimedatetime2列时,它会将模型属性转换为可以容纳.NET中整个DateTime范围的类型,即datetime2。如果DateTime属性不在SQL Server中datetime的范围内,则转换为datetime将失败。
引起异常的问题实际上不是两个可空的OpeningDateUtcClosingDateUtc列,而是CreatedOnUtc的值,在您的SQL片段中是'0001-01-01 00:00:00',即在您的模型实体中显然未初始化CreatedOnUtc。SQL Server中datetime可以存储的最早日期为1750年,因此0001年无法适合该类型(但可以适合datetime2)。
因此,解决方案是将CreatedOnUtc设置为有效的datetime值或-如您所知-在映射中将类型定义为datetime2
但我认为,如果EF默认将DateTime属性映射为datetime2,将会减少混淆。

谢谢,我已经找到了解决方案。 - Muhammad Ashikuzzaman

11

EF团队在其中一个设计会议上讨论了这个特定项目。决定保留当前的行为。这里是会议记录,可以为您提供更多背景信息。


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