调试实体框架查询

时间:2011-05-19 06:04:12

标签: debugging entity-framework entity-framework-4

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

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

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

我发现自己一次又一次地做着这件事:

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

说实话,我开始认为如果你没有数据库设计,不应该使用ORM

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

这是我在个人资料中看到的非常简单的示例以及我想要获得的结果。我的真实情况要复杂很多倍。

捕获:

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代码?

2 个答案:

答案 0 :(得分:5)

所以这就是我最终的结果。几个笔记:

  • 这在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));
            }
        }
    }
}

答案 1 :(得分:2)

嗯,可能这会有所帮助。 MSVS 2010拥有IntelliTrace。每当EF进行查询时,都会有一个带有查询的ADO.Net事件

Execute Reader "SELECT TOP (1) 
[Extent1].[id] AS [id], 
[Extent1].[Sid] AS [Sid], 
[Extent1].[Queue] AS [Queue], 
[Extent1].[Extension] AS [Extension]
FROM [dbo].[Operators] AS [Extent1]
WHERE [Extent1].[Sid] = @p__linq__0"    Command Text = "SELECT TOP (1) \r\n[Extent1].[id] AS [id], \r\n[Extent1].[Sid] AS [Sid], \r\n[Extent1].[Queue] AS [Queue], \r\n[Extent1].[Extension] AS [Extension]\r\nFROM [dbo].[Operators] AS [Extent1]\r\nWHERE [Extent1].[Sid] = @p__linq__0", 

Connection String = "Data Source=paris;Initial Catalog=telephony;Integrated Security=True;MultipleActiveResultSets=True"