10

我正在使用DBContext.Database.SqlQuery<entity>從我的C#代碼存儲庫中執行存儲過程。SQL Server將SP_EXECUTESQL識別爲對象而非過程名稱

它工作正常,但我想知道爲什麼它是執行過程如下圖所示:

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#代碼進行更改。

+0

你能告訴究竟你叫'SqlQuery'方法上面的輸出,例如'sql'字符串,'parameters'(簽名是'SqlQuery (字符串sql,params對象[]參數)') –

+0

@IvanStoev當然。 'return DataContext.Database.SqlQuery (「EXEC GetCaseList @CaseStage」,new SqlParameter(「@ CaseStage」,paramList.CaseStageID))。ToList();' – Dhwani

+0

@Dhwani,如果從C#中刪除'EXEC'會發生什麼?代碼,如下所示:'返回DataContext.Database.SqlQuery (「GetCaseList」,新的SqlParameter(「@ CaseStage」,paramList.CaseStageID))ToList();'? –

回答

6

的問題是,大多數EF的進行數據庫調用使用DbCommandCommadTypeText,所以雖然SqlServer的識別SP電話,它通過sp_executesql執行這些文本。

爲了獲得所需的行爲,命令應該是設置這樣:

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

不幸的是EF不提供指定命令類型的標準方法。我建議的解決方案是基於:

  • 自定義,以不通過定期拜訪
  • EF命令interception干擾刪除前綴和執行命令前更改命令類型使用CallPrefix StoredProcedureName SP調用SQL語法。

下面是執行:

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調用(使用你的示例):

from:

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 
+0

這與現有代碼中的最小改變完美結合。 – Dhwani

+0

Ivan,我對您的實施有一些疑問。爲什麼你創建了私有構造函數 - 「private CallInterceptor(){}'?我應該在'CallInterceptor:DbCommandInterceptor'上插入IDisposable接口並處理'Instance'對象嗎? – Dhwani

+1

@Dhwani它只是一個標準的單例模式實現 - 私有構造函數確保只有一個實例是從類本身創建的。這個類本身是無狀態的,因此不需要一次性使用。 –

-1

我覺得這是因爲EF需要動態生成SQL命令,所以需要使用exec sp_executesql來動態執行sql。

+0

但我只是執行過程而不是查詢語句。 – Dhwani

0

有如下幾個原因:

的主要原因(1):的TSQL字符串是建立只有一次,每一次相同的查詢調用與sp_executesql的後,SQL Server檢索查詢計劃從緩存中重新使用它。

(2): sp_executesql允許參數化語句,因此它在SQL注入方面比EXEC更安全。

例子進行比較,請查看以下鏈接: comparision of EXEC and EXEC sp_executesql

+0

是的,這是真的,但我的觀點是SQL Server Profiler將SP_EXECUTESQL作爲對象,而不是過程名稱(GetCaseList)作爲對象。如何使SQL Server Profiler將過程名稱視爲對象而不是SP_EXECUTESQL? – Dhwani

+0

這是跟蹤的配置文件的默認行爲,但如果您想通過應用程序的存儲過程進行過濾,請參閱以下鏈接:http://www.connectsql.com/2011/10/sql-server-using-sql -profiler-to.html –

+0

是的,我們可以這樣做。但是,我從客戶DBA獲取特定持續時間的跟蹤數據。所以,在那個追蹤中,我將擁有所有程序,選擇語句和多個數據庫的其他內容。所以,我不會爲每個程序的每一個呼叫準確計數。 – Dhwani

2

使用sp_executesql的的/被intentional.It觀察到有時在ADO.net生成的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

相關問題