C# SQL参数direction.InputOutput/SqlCommand.ExecuteOnQuery()错误

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

我有一个非常奇怪的问题,只有当所讨论的代码处于高负载情况时才会发生。我的ASP.NET web客户端C代码调用具有输出参数的T-SQL存储过程

在高负载下,返回的数据有时无法返回到调用的C代码。我已将所有相关代码提取到下面的示例中

存储过程:

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