如何跟踪 documentdb Linq 提供程序生成的查询?

How can I trace the query produced by the documentdb Linq provider?

如何在将 linq 语句发送到服务器之前查看文档数据库 sql 查询(在字符串中)?

_documentClient.CreateDocumentQuery<MyType>(
                        UriFactory.CreateDocumentCollectionUri(DatabaseName,
                            CollectionName)).Where(....).SelectMany(...)

我想将其用于跟踪目的。

您可以在 DocumentDB 查询上调用 ToString() 以获取通过网络发送的 LINQ 表达式的 SQL 翻译。

string sql = client.CreateDocumentQuery<MyType>(collectionUri).Where(t => t.Name = "x").ToString();
// sql is somthing like SELECT * FROM c WHERE c["Name"] = "x"

您可以只使用 Fiddler 并在请求发送后查看 JSON。

在此处查看示例:

CosmosDB\DocumentDB Generated SQL Query

接受的答案有点奇怪:这意味着您必须记录您编写的每一段 Linq 代码。

(使用 AspNetCore DI)。 知道 Microsoft.Azure.Cosmos.CosmosClient 包含一个 HttpClient,我们可以做的第一件事就是搜索是否可以传递我们自己的 HttpClient.

我们可以。例如,通过 CosmosClientBuilder 并使用注入的 IHttpClientFactory :

.WithHttpClientFactory(() => httpClientFactory.CreateClient("Client"))

如果您的日志过滤器配置正确,您将在控制台中看到请求/响应。

现在,我们可以在 ConfigureServices 中以这种方式将 DelegatingHandler 添加到我们的 HttpClient 中,或者在 .NET 6 中以这种方式添加 builder.Services :

services.AddTransient<NoSqlLoggingDelegatingHandler>();
services.AddHttpClient("Client")
    .AddHttpMessageHandler<NoSqlLoggingDelegatingHandler>();

或者查明 CosmosClient 是否有添加我们自己的选项,当然有(示例来自 CosmosClientBuilder)。

.AddCustomHandlers(
    new NoSqlLoggingDelegatingHandler(loggerFactory.CreateLogger<NoSqlLoggingDelegatingHandler>()))

有了这个,我们可以拦截发送的请求和响应:

public override async Task<ResponseMessage> SendAsync(RequestMessage request, CancellationToken cancellationToken)
{
    _logger.LogInformation("Requesting {uri}.\n{query}",
            request.RequestUri, await GetQueryAsync(request.Content));

    ResponseMessage response = await base.SendAsync(request, cancellationToken);

    _logger.LogInformation("Requested {uri} - {status}",
    response.RequestMessage.RequestUri, response.StatusCode);

    return response;
}

并且GetQueryAsync使用System.Text.Json读取请求的主体流:

private static async ValueTask<string?> GetQueryAsync(Stream content)
{
    string? stringContents;

    // Create a StreamReader with leaveOpen = true so it doesn't close the Stream when disposed
    using (StreamReader sr = new StreamReader(content, Encoding.UTF8, true, 1024, true))
    {
        stringContents = await sr.ReadToEndAsync();
    }

    content.Position = 0;

    if (string.IsNullOrEmpty(stringContents))
    {
        return null;
    }

    try
    {
        using JsonDocument parsedJObject = JsonDocument.Parse(stringContents);

        return parsedJObject.RootElement.GetProperty("query").GetString();
    }
    catch (KeyNotFoundException)
    {
        return stringContents;
    }
    // Not a JSON.
    catch (JsonException)
    {
        return stringContents;
    }
}

好了。我尝试缩短代码,例如,我没有添加检查日志级别是否启用的条件,以避免做无用的工作。