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

我正在使用DBContext.Database.SqlQuery从我的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#代码进行更改。

问题是大多数EF执行的数据库调用都使用带有CommadType Text DbCommand ,因此尽管SqlServer识别SP调用,但它通过sp_executesql它们作为文本执行。

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

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

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

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

这是实施:

 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 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("EXEC GetCaseList @CaseStage", new SqlParameter("@CaseStage", paramList.CaseStageID)).ToList(); 

至:

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

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

 EXEC GetCaseList @CaseStage = 9 

在entity framework/ 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来了解查询执行模式 。

有关查询参数化的更多信息,请参见

原因如下:

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

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

比较示例请查看以下链接: EXEC和EXEC sp_executesql的比较

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