SQL Server将SP_EXECUTESQL识别为对象而不是过程名称

时间:2016-11-15 10:04:12

标签: c# sql-server asp.net-mvc entity-framework stored-procedures

我正在使用DBContext.Database.SqlQuery<entity>从我的C#代码库执行存储过程。

它工作正常,但我想知道它为什么执行如下程序:

exec sp_executesql N'EXEC GetCaseList @CaseStage',N'@CaseStage int',@CaseStage=9

而不是

EXEC GetCaseList @CaseStage = 9

有没有办法让我的所有程序从c#执行,就像这样 EXEC GetCaseList @CaseStage = 9而不是exec sp_executesql N'EXEC GetCaseList @CaseStage',N'@CaseStage int',@CaseStage=9

如何使SQL Server Profiler将过程名称视为对象而不是SP_EXECUTESQL?

注意:我想从c#执行EXEC GetCaseList @CaseStage = 9的过程,因为我通过表格格式的 SQL Server Profiler 保存跟踪数据。在 ObjectName 列中,它将sp_executesql显示为对象而不是过程名称(GetCaseList)作为对象。 我只能通过c#代码进行更改。

4 个答案:

答案 0 :(得分:6)

问题是大多数EF执行的数据库调用都使用DbCommand Text CommadType,因此虽然SqlServer识别SP调用,但它会通过sp_executesql将其作为文本执行

要获得所需的行为,应该以这种方式设置命令:

DbCommand command = ...;
command.CommandText = "StoredProcedureName";
command.CommandType = CommadType.StoredProcedure;

不幸的是,EF没有提供指定命令类型的标准方法。我建议的解决方案基于:

  • 使用CallPrefix StoredProcedureName自定义SP调用SQL语法,以便不干扰常规调用
  • EF命令interception删除前缀并在执行命令之前更改命令类型。

以下是实施:

using System.Data;
using System.Data.Common;
using System.Data.Entity.Infrastructure.Interception;

public static class Sp
{
    public const string CallPrefix = "CallSP ";

    public static string Call(string name) { return CallPrefix + name; }

    public class CallInterceptor : DbCommandInterceptor
    {
        public static void Install()
        {
            DbInterception.Remove(Instance);
            DbInterception.Add(Instance);
        }

        public static readonly CallInterceptor Instance = new CallInterceptor();

        private CallInterceptor() { }

        static void Process(DbCommand command)
        {
            if (command.CommandType == CommandType.Text && command.CommandText.StartsWith(Sp.CallPrefix))
            {
                command.CommandText = command.CommandText.Substring(Sp.CallPrefix.Length);
                command.CommandType = CommandType.StoredProcedure;
            }
        }

        public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
        {
            Process(command);
            base.ReaderExecuting(command, interceptionContext);
        }
    }
}

您只需要将上述类添加到项目中,只需调用Sp.CallInterceptor.Install()一次,例如在DbContext静态构造函数中:

public class YourDbContext : DbContext
{
    static YourDbContext()
    {
        Sp.CallInterceptor.Install();
    }
    // ...
}

然后像这样更改你的SP调用(使用你的样本):

从:

return DataContext.Database.SqlQuery<CaseList>("EXEC GetCaseList @CaseStage", 
    new SqlParameter("@CaseStage", paramList.CaseStageID)).ToList();

为:

return DataContext.Database.SqlQuery<CaseList>(Sp.Call("GetCaseList"), 
    new SqlParameter("@CaseStage", paramList.CaseStageID)).ToList();

将生成(paramList.CaseStageID == 9):

EXEC GetCaseList @CaseStage = 9

答案 1 :(得分:2)

在实体框架/ ADO.net中使用sp_executesql是故意的。据观察,有时在生成的sql中,EF在直接执行查询和有时使用sp_executesql之间起决定性作用。当存在时,sp_executesql会起作用。客户端参数化有助于重用一个参数化编译计划。 如果没有指定参数,则SQL Server会尝试执行自动参数化,以帮助重用查询计划。

似乎决定使用sp_executesql或直接sql批处理由ADO.Net的SQLCommand对象控制。按照表格数据流(TDS)显示,只有两种方法可以执行SQL查询 - 使用RPC执行SQL存储过程并使用SQL Batch for T-SQL。因此,当我们有参数化查询时,我们倾向于使用RPC并调用sp_executesql.More关于查询execution pattern

有关查询参数化here

的详细信息

答案 2 :(得分:0)

有以下几个原因:

主要原因(1): TSQL字符串只构建一次,之后每次使用sp_executesql调用相同的查询时,SQL Server都会从缓存中检索查询计划并重新使用它。 / p>

(2): sp_executesql允许对语句进行参数化,因此在SQL注入方面它比EXEC更安全。

比较示例请点击以下链接: comparision of EXEC and EXEC sp_executesql

答案 3 :(得分:-1)

我认为这是因为EF需要动态生成SQL命令,因此需要使用exec sp_executesql来动态执行sql。