SQL Server日期时间类型的默认值错误

时间:2019-05-16 14:24:00

标签: c# sql-server entity-framework-core entity-framework-core-2.2

我们假设一个Category n-m Blog关系:

CREATE TABLE Categories(
    Id INT IDENTITY(1,1) NOT NULL,
    Title NVARCHAR(MAX) NOT NULL,
    Created datetime NOT NULL,
    CONSTRAINT PK_Categories PRIMARY KEY(Id)
)
CREATE TABLE Posts(
    Id INT IDENTITY(1,1) NOT NULL,
    Subject NVARCHAR(MAX) NOT NULL,
    Body NVARCHAR(MAX) NULL,
    CONSTRAINT PK_Posts PRIMARY KEY (Id)
)
CREATE TABLE PostCategory(
    CategoryId INT NOT NULL,
    PostId INT NOT NULL,
    CONSTRAINT PK_PostCategory PRIMARY KEY (CategoryId, PostId),
    FOREIGN KEY ([CategoryId]) REFERENCES [Categories]([Id]),
    FOREIGN KEY ([PostId]) REFERENCES [Posts]([Id])
)

搭建数据库后(请参见https://docs.microsoft.com/en-us/ef/core/get-started/aspnetcore/existing-db#reverse-engineer-your-model),我开始了第一个查询:

var postsWithLatestCategoryTimestamp = (
        from post in db.Posts
        let latestCategoryTimestamp =
            (
                from relation in post.PostCategory
                orderby relation.Category.Created descending
                select relation.Category.Created
            )
            .FirstOrDefault()
        select new
        {
            post.Id, post.Subject, latestCategoryTimestamp
        }
    ).ToList();

我知道了

  

System.Data.SqlClient.SqlException :“从字符串转换日期和/或时间时转换失败。”

原因: EntityFramework Core使用错误的默认值('0001-01-01T00:00:00.0000000'.而不是'1753-01-01T00:00:00.000'):

SELECT [post].[Id], [post].[Subject], COALESCE((
    SELECT TOP(1) [relation.Category].[Created]
    FROM [PostCategory] AS [relation]
    INNER JOIN [Categories] AS [relation.Category] ON [relation].[CategoryId] = [relation.Category].[Id]
    WHERE [post].[Id] = [relation].[PostId]
    ORDER BY [relation.Category].[Created] DESC
), '0001-01-01T00:00:00.0000000') AS [latestCategoryTimestamp]
FROM [Posts] AS [post]

如何在不接触现有数据库的情况下进行修复?


顺便说一下,OnModelCreating()的脚手架很好:

modelBuilder.Entity<Categories>(entity =>
{
    entity.Property(e => e.Created).HasColumnType("datetime");
    //...
});

1 个答案:

答案 0 :(得分:3)

它看起来像是EF Core错误,但对我来说,它更像是错误的查询。

想象一下,EF Core以某种方式将“正确”的CLR DateTime默认转换为SqlServer的最小支持日期。得到具体结果时,如何知道latestCategoryTimestamp是否为默认值?检查SqlServer分钟日期?如果要对内存数据库或其他数据库运行查询该怎么办?也许您会说他们必须将其转换回内存中?为什么数据库最小值应映射到default(DateTime)?那么DateTime.MinValue怎么样?

结论是default(DateTime)没有正确的映射。问题是,为什么要使用“魔术”值来表示缺失值?数据库为此提供了NULL和C#(CLR)-可为空的类型。

因此,最合乎逻辑的方法是使用可为空的类型。您只需更改查询内容,即可在此处DateTime?添加一个强制转换

select (DateTime?)relation.Category.Created

但是由于这里您只需要 maximum 日期,因此使用Max方法就足够了(但同样会出现可空的重载):

let latestCategoryTimestamp = post.PostCategory.Max(link => (DateTime?)link.Category.Created)

但是,无论选择哪种方法,那么您需要做的就是考虑实例化对象的Nullable<DateTime>属性。好消息是null将指示缺少的帖子类别,而不管数据库类型如何。