Как вызвать UDF с помощью LINQ в Cosmos DB SDK v3?

#linq #azure-cosmosdb

#linq #azure-cosmosdb

Вопрос:

В документации по переводу LINQ в SQL для Cosmos DB говорится:

Определяемая пользователем функция расширения функции: поддерживает перевод из метода заглушки UserDefinedFunctionProvider.Вызов соответствующей пользовательской функции.

Однако эта функция не является общедоступной в .NET SDK v3 (хотя она есть в версии v2). Итак, каков обходной путь, пока ошибка не будет исправлена?

Ответ №1:

Существует несколько обходных путей:

  1. Не используйте LINQ.
  2. Используйте LINQ, но без вызова UDFS, затем получите строку запроса с помощью query.ToQueryDefinition.QueryText и манипулируйте ею, чтобы вставить вызовы UDF в нужные вам места, а затем оценить строку SQL (фу!).
  3. Взломайте его. Сначала добавьте фиктивный метод:
     public static object Invoke(string udfName, object[] arguments) { return null; }
  

Используйте этот метод в своих лямбда-выражениях всякий раз, когда вы бы использовали в противном случае UserDefinedFunctionProvider.Invoke . Внедрите, ExpressionVisitor который заменяет все вхождения, MethodCallExpression mce где mce.Method соответствует фиктивному методу, на

 Expression.Call(null, udfMethod, mce.Arguments[0], mce.Arguments[1])
  

где udfMethod находится

 MethodInfo udfMethod = typeof(CosmosClient).Assembly
                .GetType("Microsoft.Azure.Cosmos.Linq.UserDefinedFunctionProvider", throwOnError: true)
                .GetMethod("Invoke", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
  

Используйте полученное выражение в вашем Where , Select и т.д.

(Добавьте модульный тест, чтобы проверить, когда ошибка была исправлена и хак может быть удален.)

Ответ №2:

Я использую Microsoft.Azure.Cosmos 3.22.1 SDK для моего примера с UDF (определяемыми пользователем функциями), и он отлично работал.

Я использовал пример от Microsoft.

Конечно, также возможно использовать UDF в сочетании с предложениями where.

 CosmosClient client = new(endpoint, key);

IQueryable<Product> queryable = client
    .GetContainer("database", "products")
    .GetItemLinqQueryable<Product>()
    .Select(b => new Product{ id = b.id, price = b.price, priceWithTax = CosmosLinq.InvokeUserDefinedFunction("tax", b.price).ToString() });

var productIterator = queryable.ToFeedIterator<Product>();
while (productIterator.HasMoreResults)
{
    var responseMessage = await productIterator.ReadNextAsync();
    Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(responseMessage));
}