我正在尝试使用.Net Core中的以下代码执行SP
using (DBContext context = new DBContext()){
{
using (var command = context.Database.GetDbConnection().CreateCommand())
{
command.CommandText = "Sp_Name";
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("@input", SqlDbType.VarChar ,3) { Value = InputValue });
command.Parameters.Add(new SqlParameter("@Return_Value", SqlDbType.VarChar, 3) { Value = string.Empty });
context.Database.OpenConnection();
var dataReader = command.ExecuteReader();
if (dataReader.Read())
{
var code = dataReader.GetString(dataReader.GetOrdinal(""));
}
}}
该查询适用于某些输入参数,但为某些参数抛出异常,例如:
- 这种情况在EF代码和SQL中运行良好
SP - exec Sp_Name @input = 'PDX', @Return_Value = ''
--Result (No Column Name) - '3I9'
- 此方案在EF代码中不起作用,但在SQL中运行良好
SP - exec Sp_Name @input = 'N01', @Return_Value = ''
--Result (No Column Name) - 'WE5'
异常消息
System.Data.SqlClient.SqlException (0x80131904): Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. ---> System.ComponentModel.Win32Exception (0x80004005): The wait operation timed out
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
at System.Data.SqlClient.SqlDataReader.get_MetaData()
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds)
at System.Data.SqlClient.SqlCommand.ExecuteScalar()
at Mednax.ReferringPhysician.Data.PdxService.getGPMSCode(String practiceCode) in C:\Work\GIT\ReferringPhysician2\Mednax.ReferringPhysician.WebAPI\Mednax.ReferringPhysician.Data\PdxService.cs:line 971
ClientConnectionId:199f2b1a-cb1b-4752-8632-9f2c54bcefd8
Error Number:-2,State:0,Class:11
堆栈跟踪:
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
at System.Data.SqlClient.SqlDataReader.get_MetaData()
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds)
at System.Data.SqlClient.SqlCommand.ExecuteScalar()
SP如下所示:
SP -
(
@Input varchar(3),
@Return_Value varchar(3) output
)
AS
SET NOCOUNT ON
SET @Return_Value = NULL
SELECT TOP 1 @Return_Value = pacl.P_Code
FROM TABLEA pacl with (nolock)
LEFT OUTER JOIN TABLEB rpp with (nolock)
ON rpp.Code = pacl.Code
AND rpp.P_Code = @Input
WHERE rpp.P_Code IS NULL
ORDER BY pacl.P_Code
IF @@Rowcount = 0 SET @Return_Value = '***'
Select @Return_Value
Targetsite of Exception消息中的详细信息:
由于参数嗅探,您在此处看到的内容似乎是一个错误的查询计划缓存条目,由于灾难性的查询计划导致超时。参数嗅探的问题在于,当没有现有的操作查询计划(与当前执行模式匹配)时,它会根据它看到的第一个参数值生成查询计划。如果您有严重偏差的数据,生成的查询计划可能适用于某些值,但对其他值则是灾难性的。例如,考虑具有一个值的3行和具有另一个值的300万行的场景。如果您根据“3行”值生成查询计划,它可能会针对该数量进行优化的决策 - 它可以在3,30和300可行的情况下正常工作 - 但是300万可能会崩溃。反之亦然。在Stack Overflow中,我们将其称为“Jon Skeet问题”:Jon(用户页面上排名第一的用户)与一个全新的1-rep用户的数据分布非常不同,Jon的查询计划很糟糕1 -rep用户,反之亦然。
幸运的是,SQL Server有一个查询提示:OPTIMIZE FOR
/ UNKNOWN
。最简单的用法是将OPTION ( OPTIMIZE FOR UNKNOWN )
添加到受影响的查询中;这指示它不会根据生成查询时看到的参数值大幅偏向查询计划。如果只有一些参数存在问题,您也可以指定单个参数(例如,我们为@userId
)。
所以;为什么这可能在SSMS(查询分析器)和.NET中工作,而不是.NET核心?我认为这里的问题是不同的SET
选项。 various SET
options定义了执行模式;其中一些选项可能会影响查询生成,因此对于具有不同SET
选项的两个客户端可能需要单独的计划。这意味着.NET Core可以有效地为.NET提供不同的查询计划缓存,因此:当一个工作时,另一个工作失败。但是:这并不意味着一个人“更糟糕”;相反,它只是意味着其中一个碰巧生成了导致灾难性计划的数据查询计划。同样的问题也可能在计划缓存由于某种原因(通常只是:逐渐的数据漂移)失效的随机时间影响 - 就像最尴尬的用户(等)正在使用该网站一样。参数嗅探问题通常不会立即出现 - 它们在任何人部署任何东西后的4天内在半夜发生。