我们假设一个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");
//...
});
答案 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
将指示缺少的帖子类别,而不管数据库类型如何。