I have the following method which uses a transaction.
private string getDocumentDetailsByNumber(string DocNumber)
{
SqlTransaction transaction = DALDBConnection.SqlConnection.BeginTransaction();
try
{
DataSet DocNum = new DataSet();
string sDocNumber = "";
string[] taleNamesDoc = new string[1];
taleNamesDoc[0] = "docnumber";
SqlParameter[] paramDoc = new SqlParameter[1];
paramDoc[0] = new SqlParameter("#DocumentNumber", DocNumber.ToString().Trim());
SqlHelper.FillDataset(transaction, CommandType.StoredProcedure, "spGetDocumentDetailsByNumber", DocNum, taleNamesDoc, paramDoc);
string docTitle = DocNum.Tables["docnumber"].Rows[0][0].ToString();
transaction.Commit();
return docTitle;
}
catch (Exception ex)
{
transaction.Rollback();
throw ex;
}
}
after running the method several times, user ended up getting the error message below.
the timeout period elapsed prior to obtaining a connection from the
pool
Error occurred because I haven't closed the connection and the connection pool has over flown.
I tried to close the connection before committing the transaction.
transaction.Connection.Close();
transaction.Commit();
Then got the following error.
This SqlTransaction has completed; it is no longer usable
How can I close the connection to avoid the error?
You cannot exhaust your pool by using a single connection. You need to close all connections you are using. Preferably after the transaction has ended one way or another. using blocks are your friend for almost all database related objects.
By the way:
throw ex;
This damages your exception by replacing the original stacktrace. Use:
throw;
to rethrow the exception you caught unchanged.
As mentioned, you should dispose of the connection properly. I've modified your code to demonstrate. Please note you will need to substitute the connection string with yours.
private string getDocumentDetailsByNumber(string DocNumber)
{
using (var connection = new SqlConnection("My Connection String"))
{
SqlTransaction transaction = connection.BeginTransaction();
DataSet DocNum = new DataSet();
string sDocNumber = "";
string[] taleNamesDoc = new string[1];
taleNamesDoc[0] = "docnumber";
SqlParameter[] paramDoc = new SqlParameter[1];
paramDoc[0] = new SqlParameter("#DocumentNumber", DocNumber.ToString().Trim());
SqlHelper.FillDataset(transaction, CommandType.StoredProcedure, "spGetDocumentDetailsByNumber", DocNum, taleNamesDoc, paramDoc);
string docTitle = DocNum.Tables["docnumber"].Rows[0][0].ToString();
transaction.Commit();
return docTitle;
} // Connection is disposed and cleaned up.
}
Opening new connections are cheap and should not be frowned upon. Each one you call the database you should open a new one like this. By maintaining a connection and not disposing of it, you are taking resources away from the database as well. It does not have an infinite amount of connections in its pool that can be used at once.
edit
Removed try/catch as mentioned in comments. If an exception is thrown while in the using block, a rollback will occur and the exception passed up the stack.
Have you considered CALLNIG CLOSE ON IT? Would be obvious to close a connection or?
Anything that implements IDIsposable should be disposed, btw., not just closed. And SqlConnection implements IDisposable. THis has nothing to do with SqlTransaction - you violae a fundamental rule of the .NET world by not disposing a disposable routine.
Related
I'm creating database, using ADO.NET. Basically, I'm executing SQL commands in next way:
private bool ExecuteSqlCommand(string command)
{
var success = true;
using (var connection = GetSqlConnection())
{
if (connection == null)
return false;
using (var myCommand = new SqlCommand("query", connection))
{
try
{
connection.Open();
myCommand.CommandText = command;
myCommand.ExecuteNonQuery();
}
catch (Exception ex)
{
success = false;
Log.LogMessage(string.Format("Unable to execute SQL command: {0}", ex.Message));
}
}
}
return success;
}
GetSqlConnection just gets some proper SqlConnection with connection string like
"Server={0}\\{1};User Id={2};Password={3};Application Name={4};"
It works well, and executes command properly, with one exception - according to Sql Manager Studio activity monitor, it remains as active connection even after method was executed.
According to question Why does my SqlConnection remain in the SSMS Processes list after closing it?, this is correct behavior, since connection might be reused. But the serius issue is, that later, READ_COMMITTED_SNAPSHOT command will be called for this database, while using different SqlConnection. Which leads to exception, since READ_COMMITTED_SNAPSHOT requires, that connection, which is used to call this command, should be the only connection to database.
I can't reuse this connection for further operations with database, since I use different connection string for them, with database specified as InitialCatalog (obviously, I can't use it, while database doesn't exist).
So, can I somehow remove this initial connection?
Execute SqlConnection.ClearPool This will mark all connections to be discarded instead of recycled.
I'm having a serious issue with my app. It builds a lot of MySql connections and then it's causing a crash.
I build every method like that:
MySqlConnection connect = new MySqlConnection(
local_connection_string
); //this is global variable.
protected void sample()
{
try
{
connect.Open();
MySqlCommand query = new MySqlCommand(
"here some mysql command"
, connect);
query.ExecuteNonQuery();
}
catch
{
}
finally
{
connect.Dispose();
connect.Close();
}
}
For some reason it's not closing any of these connections and when I keep refreshing it builds connections on the server, once limit is hit app is crashing. All connections are closed when app is shut down.
try this:
using(MySqlConnection conn = new MySqlConnetion(local_connection_string)
{
conn.open();
MySqlCommand query = new MySqlCommand(
"here some mysql command"
, connect);
query.ExecuteNonQuery();
}
using(resource){}: right way for IDisposable resource usage
probably need to add: Application.ApplicationExit event with MySqlConnection.ClearAllPools()
To ensure that connections are always closed, open the connection inside of a using block, as shown in the following code fragment. Doing so ensures that the connection is automatically closed when the code exits the block.
using (MySqlConnection connection = new MySqlConnection(connectionString))
{
connection.Open();
// Do work here; connection closed on following line.
}
MySQL counter part uses Connection pooling and does not close when you call close instead it puts it in the connection pool!
Make sure you First Close then Dispose the Reader, Command, and Connection object!
You can use ConnectionString Parameter "Pooling=false" or the static methods MySqlConnection.ClearPool(connection) and MySqlConnection.ClearAllPools()
and Using keyword is the right way to go with this kind of Scenario.
Just close first the connection , before calling the dispose...
finally
{
connect.Close();
connect.Dispose();
}
I'm using the library microsoft.practices.enterpriselibrary, to access a SQL Server database.
I'm wondering how to close connection when I use the ExecuteNonQuery method?
ie:
using Microsoft.Practices.EnterpriseLibrary.Data.Sql;
SqlDatabase db = null;
try
{
db = new SqlDatabase(stringConnection);
db.ExecuteNonQuery("storedprocedure", someParams);
}
catch (Exception ex)
{
}
I can't do something like
finally
{
if (db != null)
{
((IDisposable)db).Dispose();
}
}
So... how can I avoid connection leaks?
Thank you.
You can put the code inside the "using" block. This will ensure the connection closed after finishing.
using Microsoft.Practices.EnterpriseLibrary.Data.Sql;
SqlDatabase db = new SqlDatabase(stringConnection);
using (DbConnection con = db.CreateConnection())
{
db.ExecuteNonQuery("storedprocedure", someParams);
}
or you can use the con.Close().
Generally you do not need to worry about closing the connection, as Data Access Block manages connections more efficiently. check "Managing Connections" section in below link: http://msdn.microsoft.com/en-us/library/ff953187(v=pandp.50).aspx
Database db = DatabaseFactory.CreateDatabase();
DbCommand cmd = db.GetStoredProcCommand("GetProductsByCategory");
db.AddInParameter(cmd, "#requestId", DbType.Int32, requestId);
db.ExecuteNonQuery(cmd);
The Enterprise Library handles closing connections for you, except for the case of using Data Readers. When using a IDataReader, you should either use the close or a using to call the dispose (same as calling close)
Database db = DatabaseFactory.CreateDatabase();
DbCommand cmd = db.GetSqlStringCommand("Select Name, Address From Customers");
using (IDataReader reader = db.ExecuteReader(cmd))
{
// Process results
}
The dispose in this case will close the connection. They have a great section on connection handling in the documentation.
https://msdn.microsoft.com/en-us/library/ff648933.aspx
I got following error once in my application.
This SQLTransaction has completed; it is no longer usable
Stack Trace is attached below – It says about Zombie Check and Rollback.
What is the mistake in the code?
Note: This error came only once.
UPDATE
From MSDN - SqlTransaction.Rollback Method
A Rollback generates an InvalidOperationException if the connection is terminated or if the transaction has already been rolled back on the server.
From Zombie check on Transaction - Error
One of the most frequent reasons I have seen this error showing up in various applications is, sharing SqlConnection across our application.
CODE
public int SaveUserLogOnInfo(int empID)
{
int? sessionID = null;
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
SqlTransaction transaction = null;
try
{
transaction = connection.BeginTransaction();
sessionID = GetSessionIDForAssociate(connection, empID, transaction);
//Other Code
//Commit
transaction.Commit();
}
catch
{
//Rollback
if (transaction != null)
{
transaction.Rollback();
transaction.Dispose();
transaction = null;
}
//Throw exception
throw;
}
finally
{
if (transaction != null)
{
transaction.Dispose();
}
}
}
return Convert.ToInt32(sessionID,CultureInfo.InvariantCulture);
}
Stack Trace
REFERENCE:
What is zombie transaction?
Zombie check on Transaction - Error
SqlTransaction has completed
http://forums.asp.net/t/1579684.aspx/1
"This SqlTransaction has completed; it is no longer usable."... configuration error?
dotnet.sys-con.com - SqlClient Connection Pooling Exposed
Thread abort leaves zombie transactions and broken SqlConnection
You should leave some of the work to compiler, to wrap that in a try/catch/finally for you.
Also, you should expect that Rollback can occasionally throw an exception, if a problem occurs in Commit stage, or if a connection to server breaks. For that reason you should wrap it in a try/catch.
try
{
transaction.Rollback();
}
catch (Exception ex2)
{
// This catch block will handle any errors that may have occurred
// on the server that would cause the rollback to fail, such as
// a closed connection.
Console.WriteLine("Rollback Exception Type: {0}", ex2.GetType());
Console.WriteLine(" Message: {0}", ex2.Message);
}
This is copied exactly from MSDN documentation page for Rollback method.
I see that you're worried that you have a zombie transaction. In case you pasted, it doesn't sound like you have a problem. You're transaction has been completed, and you should no longer have anything to do with it. Remove references to it if you hold them, and forget about it.
From MSDN - SqlTransaction.Rollback Method
A Rollback generates an InvalidOperationException if the connection is terminated or if the transaction has already been rolled back on the server.
Rethrow a new exception to tell user that data may not have been saved, and ask her to refresh and review
Note: This error came only once.
then it is very hard to say much; it could be simply that the // Other Code etc simply took to long, and the entire thing got killed. Maybe your connection died, or an admin deliberately killed it because you were blocking.
What is the mistake in the code?
over-complicating it; it can be much simpler:
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
using(var transaction = connection.BeginTransaction())
{
try
{
sessionID = GetSessionIDForAssociate(connection, empID, transaction);
//Other Code
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}
much less code to get wrong.
I use code below can reproduce this error, I use 1000 tasks to execute Sql, after about 300 tasks Successfully Completed, lots of exception about timeout error start to occur on ExecuteNonQuery(),
then next error This SqlTransaction has completed will occur on transaction.RollBack(); and its call stack also contains ZombieCheck().
(If single program with 1000 tasks pressure not enough, you can execute multiple compiled exe file at the same time, or even use multi computers execute to one DataBase.)
So I guess one of the reason cause this error can be something wrong in Connection, then cause the transaction error happens as well.
Task[] tasks = new Task[1000];
for (int i = 0; i < 1000; i++)
{
int j = i;
tasks[i] = new Task(() =>
ExecuteSqlTransaction("YourConnectionString", j)
);
}
foreach (Task task in tasks)
{
task.Start();
}
/////////////
public void ExecuteSqlTransaction(string connectionString, int exeSqlCou)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
SqlCommand command = connection.CreateCommand();
SqlTransaction transaction;
// Start a local transaction.
transaction = connection.BeginTransaction();
// Must assign both transaction object and connection
// to Command object for a pending local transaction
command.Connection = connection;
command.Transaction = transaction;
try
{
command.CommandText =
"select * from Employee";
command.ExecuteNonQuery();
// Attempt to commit the transaction.
transaction.Commit();
Console.WriteLine("Execute Sql to database."
+ exeSqlCou);
}
catch (Exception ex)
{
Console.WriteLine("Commit Exception Type: {0}", ex.GetType());
Console.WriteLine(" Message: {0}", ex.Message);
// Attempt to roll back the transaction.
try
{
transaction.Rollback();
}
catch (Exception ex2)
{
// This catch block will handle any errors that may have occurred
// on the server that would cause the rollback to fail, such as
// a closed connection.
Console.WriteLine("Rollback Exception Type: {0}", ex2.GetType());
Console.WriteLine(" Message: {0}", ex2.Message);
}
}
}
}
Besides I find if I commit twice sequentailly will invoke this exception as well.
transaction.Commit();
transaction.Commit();
Or if Connection Closed before commit also invoke this error.
connection.Close();
transaction.Commit();
Update:
I find it strange that I create another new table and insert 500 thousand data to it,
then use 100000 tasks with select * from newtable sql, running 5 programs at the same time, this time the Timeout Error occur, but when transaction.Rollback() it didn't invoke the SQLTransaction has completed error.
but if the Timeout Error occur, jump into the catch block, and in the catch block do transaction.Commit() again, the SQLTransaction has completed error will happen.
I have experienced this error once and i was stuck and unable to know what is going wrong. Actually i was deleting a record and in the Stored procedure i was not deleting its child and specially the delete statement in Stored Procedure was inside the Transaction boundary. I removed that transaction code from stored procedure and got rid of getting this Error of “This SqlTransaction has completed; it is no longer usable.”
This message is simply because that you wrote code that throws an exception after the transaction has been already committed successfully.Please try to check the code you wrote after the Commit method or you can handle it by using Try..Catch and finally Blocks :) .
I built a parser that takes data stored in an xml file and sends it into a Microsoft Access database using linq-to-sql. I have the sql insert commands and they work... until they don't.
It's odd, I have each SQL command run (I keep them in a List and execute each command one at a time) and the first 40 or so run fine until they start hitting "unspecified error"s. The thing is, if I swallow the exception and instead have the exception catcher keep retrying, after a few seconds, they start working again. This means it's not an error of the SQL query itself (or at least how it's written).
This pattern repeats (there are thousands of inserts) many times. If I do normal exception handling, the program will just skip a few records while the error happens and keep inserting when whatever causes it temporarily goes away. if I let it run it's course, it inserts some records, skips some, inserts, skips, repeat and eventually inserts less than 2/3 of the records.
Is there any reason why my computer would only run 40 or so Inserts and then refuse to run more for a random but short interval?
I'm at a loss on what could be causing this.
The application is natively run; it does not use any server/web communication and all I found when looking for "unspecified error" pointed me to occurrences in ADO.NET applications.
Here's the code the error happens in:
public static string insertQuery(string sql)
{
string connetionString = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Documents and Settings\Owner\Desktop\Arbeit\TrademarkParserproject1\TrademarkParserproject\bin\x86\Debug\Database.accdb";
OleDbConnection connection;
OleDbDataAdapter oledbAdapter = new OleDbDataAdapter();
connection = new OleDbConnection(connetionString);
string success = "false";
try
{
connection.Open();
oledbAdapter.InsertCommand = new OleDbCommand(sql, connection);
oledbAdapter.InsertCommand.ExecuteNonQuery();
}
catch (Exception ex)
{
success = ex.ToString();
return success;
}
success = "true";
return success;
}
Note, I have the application running in X86 mode to avoid errors with the ACE.OLEDB.12.0 adapter.
One thing that stands out, is you never close/dispose your SqlConnection. OleDbDataAdapter is also disposable and should be disposed. A 'using' statement is a convenient construct here:
public static string insertQuery(string sql)
{
string connetionString = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Documents and Settings\Owner\Desktop\Arbeit\TrademarkParserproject1\TrademarkParserproject\bin\x86\Debug\Database.accdb";
using(var oledbAdapter = new OleDbDataAdapter())
using(var connection = new OleDbConnection(connetionString))
{
string success = "false";
try
{
connection.Open();
oledbAdapter.InsertCommand = new OleDbCommand(sql, connection);
oledbAdapter.InsertCommand.ExecuteNonQuery();
}
catch (Exception ex)
{
success = ex.ToString();
return success;
}
success = "true";
return success;
}
}