Дерево выражений в SQL с EF Core - PullRequest
0 голосов
/ 25 августа 2018

У нас есть столбец, в котором данные JSON хранятся в виде строки.Эти данные JSON считываются и преобразуются посредством материализации в IDictionary<string, object>.Это все работает нормально, пока я не хочу фильтровать по нему.Фильтрация применяется только после получения данных из базы данных.У нас будут миллионы записей, так что это неприемлемо.EF Core явно игнорирует мой фильтр как предложение WHERE, поскольку, вероятно, он не знает, как анализировать MethodCallExpressions.

Я ищу способ получить как можно ближе к SQL-запросу, который у меня есть ниже, с деревом выражений, которое у меня есть.

Мне нужно преобразовать это:

.Call System.Linq.Queryable.Where(
    .Constant<QueryTranslator`1[Setting]>(QueryTranslator`1[Setting]),
    '(.Lambda #Lambda1<System.Func`2[Setting,System.Boolean]>))

.Lambda #Lambda1<System.Func`2[Setting,System.Boolean]>(Setting $$it)
{
    ((System.Nullable`1[System.Int32]).If (
        $$it.Value != null && .Call ($$it.Value).ContainsKey("Name")
    ) {
        ($$it.Value).Item["Name"]
    } .Else {
        null
    } > (System.Nullable`1[System.Int32]).Constant<Microsoft.AspNet.OData.Query.Expressions.LinqParameterContainer+TypedLinqParameterContainer`1[System.Int32]>(Microsoft.AspNet.OData.Query.Expressions.LinqParameterContainer+TypedLinqParameterContainer`1[System.Int32]).TypedProperty)
    == .Constant<System.Nullable`1[System.Boolean]>(True)
}

В это:

SELECT *
FROM [Setting]
WHERE JSON_VALUE([Value], 'lax $.Name') > 1; -- [Value_Name] > 1 is also fine

С помощьюExpressionVisitor Мне удалось приблизиться к WHERE [Value] = 'Something', но это работает только для строк и отсутствует имя ключа.

1 Ответ

0 голосов
/ 25 августа 2018

Пока не получена «официальная» поддержка, вы можете отобразить JSON_VALUE, используя EF Core 2.0, представленный Отображение скалярной функции базы данных .

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

public static class MyDbFunctions
{
    [DbFunction("JSON_VALUE", "")]
    public static string JsonValue(string source, string path) => throw new NotSupportedException();
}

и, если он находится в отдельном классе, добавьте в свой контекст следующее OnModelCreating переопределение (не требуется, если метод находится в контексте):

modelBuilder.HasDbFunction(() => MyDbFunctions.JsonValue(default(string), default(string)));

Теперь вы можете использовать его внутри запросов LINQ to Entities, аналогичных EF.Functions. Просто обратите внимание, что функция возвращает string, поэтому для того, чтобы обмануть компилятор для «приведения» его к числовому типу, вы можете использовать метод двойного приведения, показанный ниже (протестирован и работает в EF Core 2.1.2):

var query = db.Set<Setting>()
    .Where(s => (int)(object)MyDbFunctions.JsonValue(s.Value, "lax $.Name") > 1);

, что соответствует желаемому

WHERE JSON_VALUE([Value], 'lax $.Name') > 1

Другой (возможно, более безопасный по типу) способ выполнить преобразование - использовать методы класса Convert (неожиданно поддерживаемые поставщиком SqlServer EF Core):

var query = db.Set<Setting>()
    .Where(s => Convert.ToInt32(MyDbFunctions.JsonValue(s.Value, "lax $.Name")) > 1);

, что переводится как

WHERE CONVERT(int, JSON_VALUE([Value], 'lax $.Name')) > 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...