Неверное значение по умолчанию для типа дата-сервер 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 будет указывать пропущенную категорию сообщения независимо от типа базы данных.

Другие вопросы по тегам