2013-03-19 72 views
1

我有執行Sql的問題,實際上是SqlServer上的存儲過程的簡單調用。SqlCommand ExecuteNonQuery拋出OutOfMemoryException

考慮下面SQL存儲過程:

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; 

現在我'調用以這種方式從C#這個程序:

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

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

    cmd.Dispose(); 
} 

內存真的開始快速增長。幾秒鐘後拋出異常。 Normaly所有這些代碼是用「的try/catch」和「用」的部分,但我簡化了這個片段,以顯示這個問題直接來自圖書館的SqlClient不從我的代碼。

+0

看起來您已經發現了一個關於ExecuteNonQuery實現的有趣觀點。但是你的問題是什麼? – 2013-03-19 13:10:01

回答

4

經過更多研究,我發現解決方案如何停止OutOfMemoryException並採取預期的TimeoutException。

由於存儲過程中使用PRINT,所以這種情況下內存正在增長。 Driever正在收集數據庫的默認輸出。因此,如果用戶沒有閱讀此內容,則可能會發生OutOfMemoryException。

根據你想要的結果可以使用兩種解決方案。當數據庫輸出是不是對你很重要,當執行需要長期您所期待Timout

第一個是好的。下面摘錄解決了這個問題的方式:

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(); 
} 

第二個是goog當您要執行,可以採取大量的時間真的耗時的過程。超時異常將永遠不會發生。要啓用此行爲,您需要在SqlConnection中的InfoMessage上附加SqlInfoMessageEventHandler。請參閱下面的代碼片段:

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); 
} 
相關問題