Domanda

I have problem with executing Sql that in fact is simple call of stored procedure on SqlServer.

Consider below Sql Stored Procedure:

    CREATE PROCEDURE InfiniteLoop
    AS
    BEGIN
        DECLARE @ixi NUMERIC(38) = 0
        DECLARE @i NUMERIC(38) = 0
        WHILE 1 = 1
        BEGIN
           SET @i = @i + 1
           SET @ixi = @ixi * @i
           PRINT N'some text'
        END
    END;

Now I'am calling this procedure from C# in this manner:

public void CallProcedure()
{
   SqlCommand cmd = this._connection.CreateCommand();
   cmd.CommandType = CommandType.StoredProcedure;
   command.CommandText = 'InfiniteLoop';

   //Line below throws OutOfMemoryException
   cmd.ExecuteNonQuery();

   cmd.Dispose();
}

Memory starts growing realy fast. After few seconds exception is thrown. Normaly all this code is using 'try/catch' and 'using' sections, but I have simplified this snippet to show that problem comes from SqlClient library not from my code directly.

È stato utile?

Soluzione

After more research I have found solution how to stop OutOfMemoryException and take expected TimeoutException.

I this case memory is growing because of PRINT used in stored procedure. Driever is collecting by default output from database. So if user is not reading this then OutOfMemoryException can occur.

Two solutions can be used depending on result that you want.

First one is good when Database Output is not important to you and you are expecting Timout when execution takes to long. Snippet below solves problem in this manner:

public void CallProcedure()
{
   // Line below release all Errors/PRINT output from command. Command is now
   // not collecting them, so memory is not growing. 
   // CommandTimeout will be thrown after preset value on command object.
   this._connection.FireInfoMessageEventOnUserErrors = true;

   SqlCommand cmd = this._connection.CreateCommand();
   cmd.CommandTimeout = 15;
   cmd.CommandType = CommandType.StoredProcedure;
   command.CommandText = 'InfiniteLoop';

   //Line below throws OutOfMemoryException
   cmd.ExecuteNonQuery();

   cmd.Dispose();
}

Second one is goog when you want to execute realy time consuming procedures that can take a lot of time. Timeout exception will never occur. To enable this behavior you need to attach SqlInfoMessageEventHandler on InfoMessage in SqlConnection. See snippet below:

public void CallProcedure()
{
   // By attaching this event no Timeout exception on ExecuteNonQuery occur
   // Your ExecuteNonQuery can hang infinitly!
   // If you need Timeout then you need to write you own handler from different thread
   this._connection.InfoMessage += new SqlInfoMessageEventHandler(OnInfoMessage);

   // Line below release all Errors/PRINT output from command. Command is now
   // not collecting them so memory is not growing.
   this._connection.FireInfoMessageEventOnUserErrors = true;

   SqlCommand cmd = this._connection.CreateCommand();

   // In this case Timeout will never occur
   cmd.CommandTimeout = 15;
   cmd.CommandType = CommandType.StoredProcedure;
   command.CommandText = 'InfiniteLoop';

   //Line below throws OutOfMemoryException
   cmd.ExecuteNonQuery();

   cmd.Dispose();

   this._connection.InfoMessage -= new SqlInfoMessageEventHandler(OnInfoMessage);
}

void OnInfoMessage(object sender, SqlInfoMessageEventArgs e)
{
   System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString()+": "+ e.Message);
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top