C# SQL参数direction.InputOutput/SqlCommand.ExecuteOnQuery()错误
我有一个非常奇怪的问题,只有当所讨论的代码处于高负载情况时才会发生。我的ASP.NET web客户端C代码调用具有输出参数的T-SQL存储过程 在高负载下,返回的数据有时无法返回到调用的C代码。我已将所有相关代码提取到下面的示例中 存储过程:C# SQL参数direction.InputOutput/SqlCommand.ExecuteOnQuery()错误,c#,asp.net,sql-server,stored-procedures,sqlcommand,C#,Asp.net,Sql Server,Stored Procedures,Sqlcommand,我有一个非常奇怪的问题,只有当所讨论的代码处于高负载情况时才会发生。我的ASP.NET web客户端C代码调用具有输出参数的T-SQL存储过程 在高负载下,返回的数据有时无法返回到调用的C代码。我已将所有相关代码提取到下面的示例中 存储过程: CREATE PROCEDURE GetLoginName @LoginId BIGINT, @LoginName VARCHAR(50) OUTPUT AS SET NOCOUNT ON SELECT @LoginName
CREATE PROCEDURE GetLoginName
@LoginId BIGINT,
@LoginName VARCHAR(50) OUTPUT
AS
SET NOCOUNT ON
SELECT @LoginName = LoginName
FROM Logins
WHERE Id = @LoginId
SET NOCOUNT OFF
GO
数据库基类:
public class DatabaseContextBase : IDisposable
{
private SqlConnection _connection;
private string _connectionString;
private SqlInt32 _returnValue;
private int _commandTimeout;
private static int _maxDatabaseExecuteAttempts = 3;
private static int s_commandTimeout = 30;
protected DBContextBase()
{
// try and get the connection string off the identity first...
string connectionString = GetConnectionStringFromIdentity();
if(connectionString != null)
{
ConstructionHelper(connectionString, s_commandTimeout);
}
else
{
// use the initialised static connection string, and call the other overload
// of the constructor
ConstructionHelper(s_connectionString, s_commandTimeout);
}
}
private void ConstructionHelper( string connectionString, int commandTimeout )
{
// store the connection string in a member var.
_connectionString = connectionString;
// store the timeout in a member var.
_commandTimeout = commandTimeout;
}
public static string GetConnectionStringFromIdentity()
{
IIdentity identity = Thread.CurrentPrincipal.Identity as wxyz.Security.wxyzIdentityBase;
string connectionString = null;
if(identity != null)
{
connectionString = ((wxyz.Security.wxyzIdentityBase) identity ).ConnectionString;
}
return connectionString;
}
public void Dispose()
{
if (_connection.State != ConnectionState.Closed)
{
_connection.Close();
}
_connection.Dispose();
_connection = null;
}
protected void ExecuteNonQuery(SqlCommand command)
{
SqlConnection con = this.Connection;
lock (con)
{
if (con.State != ConnectionState.Open)
{
con.Open();
}
// don't need a try catch as this is only ever called from another method in this
// class which will wrap it.
command.Connection = con;
command.Transaction = _transaction;
command.CommandTimeout = _commandTimeout;
for (int currentAttempt = 1; currentAttempt == _maxDatabaseExecuteAttempts; currentAttempt++)
{
try
{
// do it
command.ExecuteNonQuery();
// done, exit loop
break;
}
catch (SqlException sex)
{
HandleDatabaseExceptions(currentAttempt, sex, command.CommandText);
}
}
}
}
protected void HandleDatabaseExceptions(int currentAttempt, SqlException sqlException, string sqlCommandName)
{
if (DataExceptionUtilities.IsDeadlockError(sqlException))
{
if (!this.IsInTransaction)
{
// Not in a transaction and a deadlock detected.
// If we have not exceeded our maximum number of attemps, then try to execute the SQL query again.
string deadlockMessage = string.Format("Deadlock occured in attempt {0} for '{1}':", currentAttempt, sqlCommandName);
Logging.Write(new ErrorLogEntry(deadlockMessage, sqlException));
if (currentAttempt == DBContextBase.MaxDatabaseExecuteAttempts)
{
// This was the last attempt so throw the exception
throw sqlException;
}
// Wait for a short time before trying again
WaitShortAmountOfTime();
}
else
{
// We're in a transaction, then the calling code needs to handle the deadlock
string message = string.Format("Deadlock occured in transaction for '{0}':", sqlCommandName);
throw new DataDeadlockException(message, sqlException);
}
}
else if (this.IsInTransaction && DataExceptionUtilities.IsTimeoutError(sqlException))
{
// We're in a transaction and the calling code needs to handle the timeout
string message = string.Format("Timeout occured in transaction for '{0}':", sqlCommandName);
// Raise a Deadlock exception and the calling code will rollback the transaction
throw new DataDeadlockException(message, sqlException);
}
else
{
// Something else has gone wrong
throw sqlException;
}
}
/// <summary>
/// get the SqlConnection object owned by this database (already connected to db)
/// </summary>
public SqlConnection Connection
{
get {
// check whether we've got a connection string (from either identity or static initialise)
if ( _connectionString == null )
{
throw new ArgumentNullException( "connectionString", "Connection string not set" );
}
if ( _connection != null )
{
return _connection;
}
else
{
_connection = new SqlConnection( _connectionString );
return _connection;
}
}
}
/// <summary>
/// Return value from executed stored procedure
/// </summary>
public SqlInt32 ReturnValue
{
get { return _returnValue; }
set { _returnValue = value; }
}
}
因此,在ASP.NET web客户端中使用时,它的外观如下所示:
protected string GetLoginName(long loginId)
{
SqlString loginName = SqlString.Null;
using (AuthenticationDBContext dbc = new AuthenticationDBContext())
{
dbc.GetLoginName(loginId, ref loginName);
}
return loginName.Value;
}
正如你所见,这是相当标准的东西。但是,当许多不同的用户连续快速调用AuthenticationDBContext.GetLoginName方法时,loginName对象有时为null
当SqlCommand.ExecuteOnQuery无法返回任何数据时,它不会引发异常
我已经测试了SQL,它总是找到一个我在表中插入@LoginName的值,它从不为null。因此,问题发生在SqlCommand.ExecuteOnQuery之后或之中
正如我所说的,这是一个发生了什么的例子。实际上,很多不同的存储过程都不会返回数据
还值得指出的是,当所讨论的web客户端处于重载状态时,在IIS中共享应用程序池的其他web客户端不会受到影响
我使用的是.NET4.5,我的数据库在SQLServer2008上
以前有人见过这样的东西吗?
有人能推荐一些改变吗
提前感谢,
马特
更新。谢谢你的评论。我对DatabaseContextBase类做了以下更改
private void ExecuteNonQueryImpl(SqlCommand command)
{
object _lockObject = new object();
lock (_lockObject)
{
SqlConnection con = this.GetConnection();
if (con.State != ConnectionState.Open)
{
con.Open();
}
// don't need a try catch as this is only ever called from another method in this
// class which will wrap it.
command.Connection = con;
command.Transaction = _transaction;
command.CommandTimeout = _commandTimeout;
for (int currentAttempt = 1; currentAttempt <= _maxDatabaseExecuteAttempts; currentAttempt++)
{
try
{
// do it
command.ExecuteNonQuery();
// done, exit loop
break;
}
catch (SqlException sex)
{
HandleDatabaseExceptions(currentAttempt, sex, command.CommandText);
}
}
if (!this.IsInTransaction)
{
con.Close();
}
}
}
public SqlConnection GetConnection()
{
if (this.IsInTransaction)
{
return this.Connection;
}
else
{
// check whether we've got a connection string (from either identity or static initialise)
if ( _connectionString == null )
{
string exceptionMessage = Language.Translate("DbContextNotInitialized");
throw new ArgumentNullException( "connectionString", exceptionMessage );
}
return new SqlConnection(_connectionString);
}
}
然而,在负载测试中,数据有时仍然返回为null。web客户端不在事务中工作,因此每次调用时都会创建、打开和关闭一个新的SqlConnection对象。还有一些代码区域共享DatabaseContextBase类,它们在事务中工作,因此需要原始连接属性
我想再次提到的是,我确信存储过程工作正常,因为我已经将@LoginName值插入到表中,并且它从不为null
谢谢,
Matt您的for循环定义不正确
for (int currentAttempt = 1; currentAttempt == _maxDatabaseExecuteAttempts; currentAttempt++)
这将初始化CurrentAttement为1,运行循环,增加CurrentAttement,然后检查CurrentAttement是否等于3(不是),然后退出循环。我想你想要的是
for (int currentAttempt = 1; currentAttempt <= _maxDatabaseExecuteAttempts; currentAttempt++)
当您使用command.ExecuteOnQuery时,如果您返回varchar值,则不适合使用ExecuteOnQuery。尝试使用executescalar或使用datareader/dataadapter并检查存储过程是否返回任何值。在这里,您还可以检查空值。此外,您是否尝试将锁定对象更改为任何私有对象_lockObject=new object;属性,而不是锁定conobject?Hi Paresh。为什么SqlCommand.ExecuteOnQuery不适合var char?我有许多这样访问的存储过程,在正常情况下,10到20个用户都可以正常工作。谢谢,Matt查看本文了解更多信息:您是否尝试过只进行选择并输出名称,然后通过这种方式从查询中获取名称,以查看输出参数是否存在问题?我知道。我在努力上传我的代码。
for (int currentAttempt = 1; currentAttempt <= _maxDatabaseExecuteAttempts; currentAttempt++)