Как можно преобразовать JSON_VALUE в DateTime с помощью EF Core 2.2?

Я сопоставляю JSON_VALUE, используя технику из Как написать перевод DbFunction. Поскольку не все значения в JSON являются строками, иногда требуется преобразование.

При конвертации в int все нормально:

var results = context.Set<SampleTable>()
    .Where(t1 => Convert.ToInt32(
        JsonExtensions.JsonValue(t1.SampleJson, "$.samplePath.sampleInt")) > 1);
    .ToList();

Результирующий SQL:

SELECT *
FROM [SampleTable] AS [t1]
WHERE (CONVERT(int, JSON_VALUE([t1].[SampleJson], N'$.samplePath.sampleInt')) > 1)

Однако при преобразовании в DateTime это не работает:

DateTime date = new DateTime(2019, 6, 1);
var results = context.Set<SampleTable>()
    .Where(t1 => Convert.ToDateTime(
        JsonExtensions.JsonValue(t1.SampleJson, "$.samplePath.sampleDate")) >= date);
    .ToList();

Вместо отображения JsonValue вызывается напрямую, что приводит к следующему исключению:

System.NotSupportedException HResult=0x80131515 Сообщение=Указанный метод не поддерживается. StackTrace: в JsonExtensions.JsonValue (столбец String, путь строки) в System.Linq.Enumerable.WhereEnumerableIterator1.MoveNext() at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.<_TrackEntities>d__172.MoveNext() в Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext()

Почему DateTime ведет себя иначе, чем int? Что я могу сделать, чтобы DateTime работал правильно?


person Rich Bennema    schedule 07.06.2019    source источник


Ответы (1)


Проблема в том, что не все методы Convert поддерживаются.

На самом деле ни один из них стандартно не поддерживается — EF Core позволяет поставщики баз данных, чтобы добавить метод CLR и трансляторы членов для всего, что им нравится. Например, поставщик SqlServer в настоящее время поддерживает ToByte, ToDecimal, ToDouble, ToInt16, ToInt32, ToInt64 и ToString.

Это означает, что не существует независимого от базы данных способа выполнения преобразований на стороне сервера.

Поскольку вы, похоже, используете SqlServer, в качестве обходного пути я мог бы предложить использовать неявные преобразования данных (в настоящее время поддерживаемые поставщиком SqlServer) с помощью метода «двойного приведения» из моего ответа на похожий пост, например.

.Where(t1 => (DateTime)(object)JsonExtensions.JsonValue(t1.SampleJson, "$.samplePath.sampleDate") >= date);

(object) cast используется, чтобы избежать ошибки компилятора C#. Во время преобразования запроса оба приведения будут удалены, и неявное преобразование данных SQL Server в конечном итоге выполнит эту работу.

person Ivan Stoev    schedule 09.06.2019
comment
Обратите внимание, что здесь нет неявного преобразования. Это будет строка при сравнении строк в SQL Server, поскольку JSON_VALUE возвращает строку, а EF Core отображает дату и время c# в виде строки. Это будет работать очень хорошо, если все даты JSON представлены в формате, совместимом с DateTime2FormatConst. - person Rich Bennema; 10.06.2019
comment
@RichBennema Я почти уверен, что переменная date будет связана как параметр datetime или datetime2 SQL, поэтому должно быть преобразование. - person Ivan Stoev; 10.06.2019
comment
Да вы правы. Вот команда, полученная через SQL Server Profiler: exec sp_executesql N'SELECT * FROM [SampleTable] AS [t1] WHERE (JSON_VALUE([t1].[SampleJson], N''$.samplePath.sampleDate'') >= @__date_0)',N'@__date_0 datetime2(7)',@__date_0='2019-06-01 00:00:00'. При тестировании я подтвердил стабильные результаты после обновления формата некоторых дат вперед и назад. - person Rich Bennema; 10.06.2019