Неверное значение по умолчанию для типа дата-сервер SQL Server
Давайте предположим, Category
нм 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) я начал свой первый запрос:
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 ответ
Это может выглядеть как ошибка EF Core, но для меня это больше похоже на некорректный запрос.
Представьте, что EF Core как-то переводит "правильно" в CLR DateTime
по умолчанию минимальная поддерживаемая дата SqlServer. Когда вы получите материализованный результат, как вы узнаете, если latestCategoryTimestamp
по умолчанию? Проверка на минимальную дату SqlServer? Что если вы выполняете запрос к базе данных в памяти или какой-либо другой базе данных? Может быть, вы бы сказали, что они должны преобразовать его обратно в память? Почему минимальное значение базы данных должно быть сопоставлено с default(DateTime)
? Как насчет DateTime.MinValue
затем?
Вывод заключается в том, что нет правильного отображения default(DateTime)
, Вопрос в том, зачем использовать "магическое" значение для представления отсутствующего значения? Базы данных предоставляют NULL
для этого и C# (CLR) - обнуляемые типы.
Поэтому самый логичный способ - использовать обнуляемый тип. Все, что вам нужно изменить в своем запросе, это добавить приведение к DateTime?
Вот
select (DateTime?)relation.Category.Created
Но так как все, что вам нужно здесь, это максимальная дата, было бы достаточно использовать Max
метод (но опять-таки с нулевой перегрузкой):
let latestCategoryTimestamp = post.PostCategory.Max(link => (DateTime?)link.Category.Created)
Но какой бы подход вы ни выбрали, все, что вам нужно, - это учитывать Nullable<DateTime>
свойство материализованного объекта. Хорошо, что null
будет указывать пропущенную категорию сообщения независимо от типа базы данных.