Сущность, добавляющая пользовательский метод

Мы используем newrelic для мониторинга производительности базы данных. Наши проблемы в том, что Entity Framework генерирует SQL, и у нас возникают проблемы с отслеживанием, например, где этот SQL-запрос был сгенерирован в нашей базе кода.

Допустим, приведенный ниже запрос вызывает проблемы с производительностью. Как я могу изменить сгенерированный SQL-запрос и добавить собственный комментарий к нему, например

SELECT 
    ? AS [C1], 
    [GroupBy1].[K1] AS [Destination], 
    [GroupBy1].[A1] AS [C2]
    FROM ( SELECT 
        [Extent1].[Destination] AS [K1], 
        SUM([Extent1].[SearchCount]) AS [A1]
        FROM [Flight].[Item] AS [Extent1]
        WHERE ([Extent1].[Origin] IN (?, ?)) AND ( NOT ((@p__linq__0 = ?) AND ([Extent1].[IsDomesticTurkish] = ?))) AND ([Extent1].[DestinationCity] IN (?, ?, ?, ?, ?, ?, ?, ?)) AND ([Extent1].[DestinationCity] IS NOT NULL)
        GROUP BY [Extent1...More…

Так как я могу сделать что-то вроде этого var DbContext.MyDbSet.Where(myWhereEx).AddCustomComment("Здравствуйте, это пользовательский комментарий, я напишу его из своего кода")

После этого вывод будет таким

SELECT 
    ? AS [C1], 
    [GroupBy1].[K1] AS [Destination], 
    [GroupBy1].[A1] AS [C2]
    FROM ( SELECT 
        [Extent1].[Destination] AS [K1], 
        SUM([Extent1].[SearchCount]) AS [A1]
        FROM [Flight].[Item] AS [Extent1]
        WHERE ([Extent1].[Origin] IN (?, ?)) AND ( NOT ((@p__linq__0 = ?) AND ([Extent1].[IsDomesticTurkish] = ?))) AND ([Extent1].[DestinationCity] IN (?, ?, ?, ?, ?, ?, ?, ?)) AND ([Extent1].[DestinationCity] IS NOT NULL)
        GROUP BY [Extent1...More…

--Hello this is a custom comment I will write it from my code

Мой вопрос заключается в том, как реализовать AddCustomComment и внутри AddCustomComment изменить сгенерированный sql, прежде чем он идет на сервер SQL

1 ответ

Решение

Я не знаю ни о какой встроенной функциональности EF, чтобы сделать это, но вот одна идея, как это можно сделать.

Вы можете использовать локальную переменную потока для хранения комментария (поэтому каждый поток имеет свою собственную копию этой переменной) и использовать перехватчик команд Entity Framework, чтобы добавить комментарий к команде перед ее выполнением, а затем очистить переменную комментария после выполнения команды. Вот пример реализации:

class EFCommentInterceptor : IDbCommandInterceptor {
    private static readonly ThreadLocal<string> _comment = new ThreadLocal<string>();

    internal static void SetComment(string comment) {
        _comment.Value = comment;
    }

    public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext) {
        AddComment(command);
    }

    public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext) {
        _comment.Value = null;
    }

    public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) {
        AddComment(command);
    }

    public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) {
        _comment.Value = null;
    }

    public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) {
        AddComment(command);
    }

    public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) {
        _comment.Value = null;
    }

    private void AddComment(DbCommand command) {
        if (!String.IsNullOrWhiteSpace(_comment.Value))
            command.CommandText += "\r\n\r\n-- " + _comment.Value;
    }
}

Затем добавьте метод расширения следующим образом:

static class QueryableExtensions {
    public static IQueryable<T> WithComment<T>(this IQueryable<T> query, string comment) {
        EFCommentInterceptor.SetComment(comment);
        return query;
    }
}

Зарегистрировать перехватчик:

DbInterception.Add(new EFCommentInterceptor());

И использовать это:

using (var ctx = new MyContext()) {
    ctx.MyDbSet.Where(c = c.MyColumn > 1).WithComment("Hello this is a custom comment I will write it from my code").ToArray();
    ctx.MyDbSet.Take(10).ToArray(); // no comment here
    ctx.MyDbSet.Take(10).WithComment("Again with comment").ToArray();
}
Другие вопросы по тегам