Как преобразовать число в DateTime для сравнения в запросе LINQ Entity Framework? - PullRequest
0 голосов
/ 14 февраля 2019

У меня есть две сущности в базе данных, которые содержат DateTime в двух разных форматах;SuperLogs.EndTime является нормальным DateTime, тогда как SuperLogs.Logs содержит DateTime как отдельные числовые компоненты Year (int), Month (int), Day (int) и Time (double).Что я хочу сделать в своем запросе, так это получить единственную SuperLogs сущность, последняя версия которой имеет DateTime больше, чем EndTime из superLogX.

У меня есть функция C # (GetLogDateTime) дляпреобразовать DateTime для Logs в фактический DateTime, но при передаче в следующем запросе «System.InvalidOperationException: внутренняя ошибка .25 данных поставщика .NET Framework».

var superLogNewerThanSuperLogX = await MyDbContext.SuperLogs.SingleOrDefaultAsync
(
    superLog => superLog.Logs.Max<Log, DateTime>(GetLogDateTime) > superLogX.EndTime
);

GetLogDateTime реализован следующим образом:

private static DateTime GetLogDateTime(Log log)
{
    var time = log.Time;
    var hasTime = log.Time.HasValue;
    var hours = hasTime ? Math.Floor(time.Value / 10000) : 0;
    var minutes = hasTime ? Math.Floor(time.Value / 100) - hours * 100 : 0;

    return new DateTime
    (
        log.Year,
        log.Month,
        log.Day,
        (int)hours,
        (int)minutes,
        hasTime ? (int)(time.Value - (hours * 10000 + minutes * 100)) : 0,
        hasTime ? (int)((time.Value - Math.Truncate(time.Value)) * 1000) : 0
    );
}

Я думаю, что часть проблемы заключается в том, что GetLogDateTime конвертирует только в Func<Log, DateTime>, но необходимо преобразоватьв Expression<Func<Log, DateTime>> как-то.Я не уверен, как это сделать, и если это невозможно, у меня также есть скалярная функция, реализованная в базе данных, которая выполняет ту же задачу, что и этот метод.

CREATE FUNCTION dbo.CreateDateTime
(
    @year SMALLINT,
    @month SMALLINT,
    @day SMALLINT,
    @time FLOAT
)
RETURNS DATETIME AS
BEGIN
    DECLARE @paddedYear VARCHAR(4) = REPLACE(STR(@year, 4), SPACE(1), '0')
    DECLARE @paddedMonth VARCHAR(2) = REPLACE(STR(@month, 2), SPACE(1), '0')
    DECLARE @paddedDay VARCHAR(2) = REPLACE(STR(@day, 2), SPACE(1), '0')
    DECLARE @hours FLOAT = ROUND(@time / 10000, 0, 1)
    DECLARE @hoursAndMinutes FLOAT = ROUND(@time / 100, 0, 1)
    DECLARE @hoursMinutesAndSeconds FLOAT = ROUND(@time, 0, 1)
    --DATETIME only supports 3 decimal places on seconds so VARCHAR 5 is the max needed for the string 0.xxx
    DECLARE @milliseconds VARCHAR(5) = CAST(@time - ROUND(@time, 0, 1) AS VARCHAR(5))
    RETURN IIF
    (
        @year IS NULL
        OR @month IS NULL
        OR @day IS NULL,
        NULL,
        CAST
        (
            IIF
            (
                @time IS NULL,          
                @paddedYear
                + @paddedMonth
                + @paddedDay,
                @paddedYear
                + @paddedMonth
                + @paddedDay
                + ' '
                --In ROUND, the final argument of 1 forces ROUND to always round down (desired here).
                + REPLACE(STR(@hours, 2), SPACE(1), '0')
                + ':'
                + REPLACE(STR(@hoursAndMinutes - @hours * 100, 2), SPACE(1), '0')
                + ':'
                + REPLACE(STR(@hoursMinutesAndSeconds - @hoursAndMinutes * 100, 2), SPACE(1), '0')
                + IIF
                (
                    @time <> @hoursMinutesAndSeconds,
                    '.'
                    + SUBSTRING(@milliseconds, 3, LEN(@milliseconds) - 2),
                    ''
                )
            )
            AS DATETIME
        )
    )
END

Однако это не особенно помогаетпоскольку я не могу заставить Entity Framework позволить мне это называть!На данный момент единственная жизнеспособная опция, которую я вижу, - это попытаться переместить весь мой запрос (намного больше, чем показано здесь) в базу данных в виде хранимой процедуры.Есть ли другой способ или это то, что я буду вынужден сделать?

...