Entity framework 添加自定义方法

Entity framework adding Custom method

我们正在使用 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("Hello this is a custom comment I will write it from my code")

之后输出会是这样

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 服务器

我不知道有任何 EF built-in 功能可以做到这一点,但这是如何完成的一个想法。

你可以使用thread-local变量来存储注释(所以每个线程都有它自己的那个变量的副本)并使用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();
}