调试 Entity Framework 查询

标签 debugging entity-framework entity-framework-4

这是关于特定情况的一些主观问题。对我来说,这个问题的主要目标是提醒自己编写解决方案。但是,如果已经有解决方案或替代方法,我想知道它。

我正在做一个项目,我正在使用 Entity Framework 4 进行数据库访问。数据库设计是我无法控制的。数据库是多年前设计的,在我看来,数据库设计不适合当前的数据库用途。这会导致非常复杂的查询。

这是我第一次在项目中使用 Entity Framework ,但我在针对 MS SQL Server 的开发方面拥有丰富的经验。

我发现自己一次又一次地做的是:

  • 我写了一个复杂的 L2E 查询。查询缓慢或返回错误结果
  • 我正在查看我的 L2E 查询,但我完全不知道如何改进它
  • 我启动 SQL Profiler 并捕获 EF 从我的查询中生成的 SQL
  • 我想执行该 sql 的一部分来识别出现问题的查询部分
  • 查询以带有十几个参数的 sp_executesql 形式出现,因为如果一个参数在查询中使用 3 次,L2E 会创建 3 个参数并将相同的值传递给所有参数。对每个参数进行相同的处理。
  • 现在我必须从 sp_executesql 中提取 SQL,取消转义所有转义的撇号,并将查询中的每个参数替换为其值
  • 完成此操作后,我终于可以运行部分查询并查明问题所在。
  • 我回到我的 L2E 代码,更改它以解决我发现的问题,然后循环重复。

  • 老实说,我开始认为如果您不拥有数据库设计,就不应该使用 ORM。

    除此之外,取消转义 sql 并替换参数的过程是我想要自动化的过程。目标是获得可以在 SSMS 中运行的“裸”、去参数化的 sql。

    这是一个非常简单的示例,说明了我在配置文件中看到的内容以及我想要得到的结果。我的真实案例要复杂很多倍。

    捕获:
    exec sp_executesql N'SELECT 
    [Extent1].[ProductName] AS [ProductName]
    FROM  [dbo].[Products] AS [Extent1]
    INNER JOIN [dbo].[Categories] AS [Extent2] ON [Extent1].[CategoryID] = [Extent2].[CategoryID]
    WHERE ([Extent1].[UnitPrice] > @p__linq__0) AND ([Extent2].[CategoryName] = @p__linq__1) AND (N''Chang'' <> [Extent1].[ProductName])',N'@p__linq__0 decimal(1,0),@p__linq__1 nvarchar(4000)',@p__linq__0=1,@p__linq__1=N'Beverages'
    

    期望的结果:
    SELECT 
    [Extent1].[ProductName] AS [ProductName]
    FROM  [dbo].[Products] AS [Extent1]
    INNER JOIN [dbo].[Categories] AS [Extent2] ON [Extent1].[CategoryID] = [Extent2].[CategoryID]
    WHERE ([Extent1].[UnitPrice] > 1) AND ([Extent2].[CategoryName] = N'Beverages') AND (N'Chang' <> [Extent1].[ProductName])
    

    如果没有更好的方法,我将编写代码将第一类转换为第二类,我将在此处发布解决方案。但也许它已经由某人完成了?或者也许有一个分析器或其他东西,可以给我可以在 SSMS 中部分执行的 sql 代码?

    最佳答案

    所以这就是我最终的结果。几点注意事项:

  • 这在 100% 的情况下不会起作用,但这对我来说已经足够了
  • 在可用性方面有很多需要改进的地方。目前我在桌面上放了一个编译后的二进制文件的快捷方式,剪切要转换为剪贴板的文本,双击快捷方式并粘贴结果。

  • using System;
    using System.Text.RegularExpressions;
    using System.Windows.Forms;
    
    namespace EFC
    {
        static class Program
        {
            [STAThread]
            static void Main()
            {
                try
                {
                    string input = Clipboard.GetText();
                    const string header = "exec sp_executesql N'";
    
                    CheckValidInput(input.StartsWith(header), "Input does not start with {0}", header);
    
                    // Find part of the statement that constitutes whatever sp_executesql has to execute
                    int bodyStartIndex = header.Length;
                    int bodyEndIndex = FindClosingApostroph(input, bodyStartIndex);
    
                    CheckValidInput(bodyEndIndex > 0, "Unable to find closing \"'\" in the body");
    
                    string body = input.Substring(bodyStartIndex, bodyEndIndex - bodyStartIndex);
    
                    // Unescape 's
                    body = body.Replace("''", "'");
    
                    // Work out where the paramters are
                    int blobEndIndex = FindClosingApostroph(input, bodyEndIndex + 4);
                    CheckValidInput(bodyEndIndex > 0, "Unable to find closing \"'\" in the params");
    
                    string ps = input.Substring(blobEndIndex);
    
                    // Reverse, so that P__linq_2 does not get substituted in p__linq_20
                    Regex regexEf = new Regex(@"(?<name>@p__linq__(?:\d+))=(?<value>(?:.+?)((?=,@p)|($)))", RegexOptions.RightToLeft);
                    Regex regexLinqToSql = new Regex(@"(?<name>@p(?:\d+))=(?<value>(?:.+?)((?=,@p)|($)))", RegexOptions.RightToLeft);
    
                    MatchCollection mcEf = regexEf.Matches(ps);
                    MatchCollection mcLinqToSql = regexLinqToSql.Matches(ps);
                    MatchCollection mc = mcEf.Count > 0 ? mcEf : mcLinqToSql;
    
                    // substitutes parameters in the statement with their values
                    foreach (Match m in mc)
                    {
                        string name = m.Groups["name"].Value;
                        string value = m.Groups["value"].Value;
                        body = body.Replace(name, value);
                    }
    
                    Clipboard.SetText(body);
                    MessageBox.Show("Done!", "CEF");
    
                }
                catch (ApplicationException ex)
                {
                    MessageBox.Show(ex.Message, "Error");
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Error");
                    MessageBox.Show(ex.StackTrace, "Error");
                }
            }
    
            static int FindClosingApostroph(string input, int bodyStartIndex)
            {
                for (int i = bodyStartIndex; i < input.Length; i++)
                {
                    if (input[i] == '\'' && i + 1 < input.Length)
                    {
                        if (input[i + 1] != '\'')
                        {
                            return i;
                        }
                        i++;
                    }
                }
    
                return -1;
            }
    
            static void CheckValidInput(bool isValid, string message, params object[] args)
            {
                if (!isValid)
                {
                    throw new ApplicationException(string.Format(message, args));
                }
            }
        }
    }
    

    关于调试 Entity Framework 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6054484/

    相关文章:

    sql - 如何找到未封闭的连接?超时时间已到。在操作完成之前超时时间已过或服务器没有响应

    node.js - 是否可以使用通用调试器(gdb、lldb 等)在 Electron 应用程序中调试 Node c++ 插件?

    javascript - 如何在特定行暂停JS脚本执行?

    sql - EF 迁移正在删除列并尝试将不存在的列重命名回 Id

    c# - 如何使用 Rhino 模拟 stub SingleOrDefault

    entity-framework - Asp.Net Web API错误: The 'ObjectContent` 1' type failed to serialize the response body for content type ' application/xml; charset=utf-8'

    调试由 valgrind 生成的内核

    .net - 将 F# 用于 BLL 实现是否有意义?

    entity-framework - Entity Framework - 如果 SaveChanges 失败并且我不想进行某些更改该怎么办?

    entity-framework - 如何阻止 Entity Framework 生成 ObjectContext 和 dbContext