Question:
Is there a way to stop the Command.ExecuteNonQuery() before it is completed, without using timeout?
I have created a multi-threaded MySQL program that stores and run sql statements. Allowing a group of transactions run at the same time (because they do not modify the same tables).
I have had to Disable the Timeout(set it to 0), because some SQL Statements can take several mins to run.
The problem comes when I want to stop the queries. Right now I have to wait till that current SQL Statement finished (like I said it could take several mins).
Below is code that works, from my existing knowledge (To help others):
MySqlConnectionStringBuilder ConnectionString = new MySqlConnectionStringBuilder();
ConnectionString.Server = ServerName; // ServerName is a user defined string
ConnectionString.Database = DatabaseName; // DatabaseName is a user defined string
ConnectionString.UserID = UserName; // UserName is a user defined string
if (!Password.Equals(string.Empty)) // Password is a user defined string
{ ConnectionString.Password = Password; } // If Password string is not empty, then add it.
ConnectionString.AllowUserVariables = true;
using (MySqlConnection connection = MySqlConnection(ConnectionString))
{
try
{
connection.Open();
DBTransaction Trans = connection.BeginTransaction();
using (MySqlCommand Command = connection.CreateCommand())
{
foreach(String SQLCommandString in SQLCommands) // SQLCommands is user defined List<String>
{
try
{
Command.CommandText = SQLCommandString; // SQLCommandString is a user defined string ex "UPDATE MyTable SET MyVar = 3 WHERE id = 3;"
NumOfRecordAffected = Command.ExecuteNonQuery();
}
catch (MySql.Data.MySqlClient.MySqlException ex)
{
Trans.RollBack();
// If code reaches here then there was a problem with the SQLCommandString executing.
throw ex;
}
catch (Exception ex)
{
// There was a problem other than with the SQLCommandString.
throw ex;
}
}
}
Trans.Commit();
}
You can't do this in a single threaded application, because control won't return until a) the query has completed execution or b) an exception forces control to return.
Instead, you can start and execute all transactions in a worker thread and close the connection from the original thread if needed. Calling MySqlConnection.Close() will rollback any pending transactions.
Related
I have an application in C# which receives data from different clients and insert that data into SQL Server. I get the data every second, or even faster, but I am facing some problem with my code:
static SqlConnection objSqlConn = null;
static SqlCommand objSqlCmd = null;
public static void SaveClientHistory(String strMessage, String strClientIP)
{
try
{
using (objSqlConn = new SqlConnection(strConnectionString))
{
using (objSqlCmd = new SqlCommand("procInsertHistory", objSqlConn))
{
objSqlCmd.CommandTimeout = 0;
objSqlCmd.CommandType = CommandType.StoredProcedure;
objSqlCmd.Parameters.AddWithValue("#strMessage", strMessage);
objSqlCmd.Parameters.AddWithValue("#strClientIP", strClientIP);
objSqlConn.Open();
objSqlCmd.ExecuteNonQuery();
}
}
}
catch (Exception Ex)
{
throw Ex;
}
finally
{
if(objSqlConn != null && objSqlConn.State != ConnectionState.Closed)
{
objSqlConn.Close();
objSqlConn.Dispose();
}
}
}
Different types of exceptions occurred:
The connection was not closed. The connection's current state is connecting.
Internal connection fatal error.
ExecuteNonQuery requires an open and available Connection. The connection's current state is connecting.
Please, advise me if there is any error in above code or suggest any other way to accomplish this task.
Thanks
Edited - simplified the procedure further to better troubleshoot the issue: removed the try/catch because it wasn't doing anything helpful; method is no longer static; all inputs are now passed in as parameters - including strConnectionString; the connection timeout is explicitly set; the connection is opened before the SqlCommand object is instantiated; the command timeout is now 10 seconds.
As performance is a concern of yours, note that you should not be worried about trying to keep a connection open for re-use. By default, SQL Server connection pooling is turned on, so there is no need to attempt to cache connections with your own methodology.
There seems like there is something going on besides attempting to open a connection and executing a non-query, so I tried to simplify your code a little further. I hope it helps with troubleshooting your issue.
public int SaveClientHistory(String strConnectionString, String strMessage, String strClientIP)
{
// You can double-up using statements like this (for slightly better readability)
using (SqlConnection objSqlConn = new SqlConnection(strConnectionString))
{
objSqlConn.ConnectionTimeout = 10; // Creating a connection times out after ten seconds
objSqlConn.Open();
using (SqlCommand objSqlCmd = new SqlCommand("procInsertHistory", objSqlConn))
{
objSqlCmd.CommandTimeout = 10; // Creating a command times out after ten seconds
objSqlCmd.CommandType = CommandType.StoredProcedure;
objSqlCmd.Parameters.AddWithValue("#strMessage", strMessage);
objSqlCmd.Parameters.AddWithValue("#strClientIP", strClientIP);
return objSqlCmd.ExecuteNonQuery();
}
}
}
It seems that all three errors are related with connection. In one of my applications, I implement you function like bellow. I hope this help you:
public static void SaveClientHistory(String strMessage, String strClientIP)
{
SqlConnection objSqlConn = new SqlConnection(strConnectionString);
SqlCommand objSqlCmd = new SqlCommand("procInsertHistory", objSqlConn)
objSqlCmd.CommandType = CommandType.StoredProcedure;
objSqlCmd.Parameters.AddWithValue("#strMessage", strMessage);
objSqlCmd.Parameters.AddWithValue("#strClientIP", strClientIP);
try{
objSqlConn.Open();
objSqlCmd.ExecuteNonQuery();
}
catch (Exception Ex)
{
throw Ex;
}
finally
{
if(objSqlConn.State == ConnectionState.Open)
objSqlConn.Close();
}
}
We use application in server with Sql server and it receive many SDF file from clients to update the SqlServer Data Base the update for many Tables and I use Transaction for this, the application in server used by multiple user than it possible to be two updates in the same time, and the two with transaction, the application throw an exception that table used by another transaction !!
New transaction is not allowed because there are other threads running in the session.
and the user have other task to do in the app with transaction too, for the moment if there's an update he have to wait until it finish.
Any ideas?
Update : there's some code for my fonction : this is my fonctions with transaction run in BackgroundWorker with timer :
DbTransaction trans = ConnectionClass.Instance.MasterConnection.BeginTransaction();
try
{
ClientDalc.UpdateClient(ChosenClient, trans);
// other functions with the same transaction
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
return false;
}
Update Client:
public static bool UpdateClient(ClientEntity ChosenClient, DbTransaction trans)
{
bool retVal = true;
string sql = "UPDATE ClientTable Set ........";
try
{
using (DbCommand cmd = DatabaseClass.GetDbCommand(sql))
{
cmd.Transaction = trans;
cmd.Parameters.Add(DatabaseClass.GetParameter("#Name", Client.Name));
//
cmd.ExecuteNonQuery();
}
}
catch (Exception ex)
{
retVal = false;
}
return retVal;
}
If I run any thing else in the same time that BackgroundWorker is in progress I got exception even is not a transaction
ExecuteReader requires the command to have a transaction when the connection assigned to the command is in a pending local transaction. The Transaction property of the command has not been initialized.”
Is this a totally borked posting?
The only reference I find to that error points to Entity Framework, NOT Sql server and I ahve never seen that in sql server - in fact, sql server has no appropiate conceot for session at all. They are called Conenctions there.
SqlException from Entity Framework - New transaction is not allowed because there are other threads running in the session
In that case your description and the tagging makes ZERO sense and the onyl answer is to use EntityFramework PROPERLY - i.e. not multi threaded. Open separate sessions per thread.
Try to follow the sample C# code
using (SqlConnection con = new SqlConnection("Your Connection String"))
{
using (SqlCommand cmd = new SqlCommand("Your Stored Procedure Name", con))
{
SqlParameter param = new SqlParameter();
param.ParameterName = "Parameter Name";
param.Value = "Value";
param.SqlDbType = SqlDbType.VarChar;
param.Direction = ParameterDirection.Input;
cmd.Parameters.Add(param);
cmd.ExecuteNonQuery();
}
}
Try to follow sample Stored Procedure
Create Proc YourStoredProcedureName
#ParameterName DataType(Length)
As
Begin
Set NoCount On
Set XACT_ABORT ON
Begin Try
Begin Tran
//Your SQL Code...
Commit Tran
End Try
Begin Catch
Rollback Tran
End Catch
End
Suggestions
Keep your transaction as short as possible.
When you are adding a record in table, it will definitely lock the resource until the transaction is completed. So definitely other users will have to wait.
In case of updation, you can use concurrency controls
Im making a system which should be running 24/7, with timers to control it. There are many calls to the database, and at some point, two methods are trying to open a connection, and one of them will fail. I've tried to make a retry method, so my methods would succeed. With the help from Michael S. Scherotter and Steven Sudit's methods in Better way to write retry logic without goto, does my method look like this:
int MaxRetries = 3;
Product pro = new Product();
SqlConnection myCon = DBcon.getInstance().conn();
string barcod = barcode;
string query = string.Format("SELECT * FROM Product WHERE Barcode = #barcode");
for (int tries = MaxRetries; tries >= 0; tries--) //<-- 'tries' at the end, are unreachable?.
{
try
{
myCon.Open();
SqlCommand com = new SqlCommand(query, myCon);
com.Parameters.AddWithValue("#barcode", barcode);
SqlDataReader dr = com.ExecuteReader();
if (dr.Read())
{
pro.Barcode = dr.GetString(0);
pro.Name = dr.GetString(1);
}
break;
}
catch (Exception ex)
{
if (tries == 0)
Console.WriteLine("Exception: "+ex);
throw;
}
}
myCon.Close();
return pro;
When running the code, the program stops at the "for(.....)", and the exception: The connection was not closed. The connection's current state is open... This problem was the reason why I'm trying to make this method! If anyone knows how to resovle this problem, please write. Thanks
You do
myCon.Open();
inside the for loop, but
myCon = DBcon.getInstance().conn();
outside of it. This way you try to open the same connection multiple times. If you want to protect against loss of DB connection you need to put both inside teh loop
You should move the call to myCon.Open outside the for statement or wrap myCon.Open() checking the connection state before re-opening the connection:
if (myCon.State != ConnectionState.Open)
{
myCon.Open();
}
Edited for new information
How about using Transactions to preserve data integrity, getting on-the-fly connections for multiple access and wrapping them in Using statements to ensure connections are closed? eg
Using (SqlConnection myCon = new SqlConnection('ConnectionString'))
{
myCon.Open();
var transaction = myCon.BeginTransaction();
try
{
// ... do some DB stuff - build your command with SqlCommand but use your transaction and your connection
var sqlCommand = new SqlCommand(CommandString, myCon, transaction);
sqlCommand.Parameters.Add(new Parameter()); // Build up your params
sqlCommand.ExecuteNonReader(); // Or whatever type of execution is best
transaction.Commit(); // Yayy!
}
catch (Exception ex)
{
transaction.RollBack(); // D'oh!
// ... Some logging
}
myCon.Close();
}
This way even if you forget to Close the connection, it will still be done implicitly when the connection gets to the end of its Using statement.
Have you tried adding
myCon.Close();
Into a Finally block. It looks like it is never being hit if you have an exception. I would highly recommend that you wrap the connection, command object etc in Using statements. This will ensure they are disposed of properly and the connection is closed.
I have the following code
try
{
using (var connection = new SqlConnection(Utils.ConnectionString))
{
connection.Open();
using (var cmd = new SqlCommand("StoredProcedure", connection))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
var sqlParam = new SqlParameter("id_document", idDocument);
cmd.Parameters.Add(sqlParam);
int result = cmd.ExecuteNonQuery();
if (result != -1)
return "something";
//do something here
return "something else";
}
}
//do something
}
catch (SqlException ex)
{
return "something AKA didn't work";
}
The question is: Does var connection still get closed if an unexpected error happens between the using brackets ({ })?
The problem is that most of my calls to stored procedures are made this way, and recently I have been getting this error:
System.InvalidOperationException: Timeout expired. The timeout
period elapsed prior to obtaining a connection from the pool. This
may have occurred because all pooled connections were in use and max
pool size was reached.
The other way I access the DB is through nHibernate.
using Statement (C# Reference)
The using statement ensures that Dispose is called even if an
exception occurs while you are calling methods on the object. You can
achieve the same result by putting the object inside a try block and
then calling Dispose in a finally block; in fact, this is how the
using statement is translated by the compiler. The code example
earlier expands to the following code at compile time (note the extra
curly braces to create the limited scope for the object):
Yes, if it gets into the body of the using statement, it will be disposed at the end... whether you reached the end of the block normally, exited via a return statement, or an exception was thrown. Basically the using statement is equivalent to a try/finally block.
Is that the only place you acquire a connection? Has your stored procedure deadlocked somewhere, perhaps, leaving lots of connections genuinely "busy" as far as the client code is concerned?
In terms of your connection pool running out of available connections, if you are in a distributed environment and using many applications to access SQL Server but they all use the same connection string, then they will all be using the same pool on the server. To get around this you can change the connection string for each application by setting the connection WorkstationID to the Environment.MachineName. This will make the server see each connection as different and provide a pool to each machine instead of sharing the pool.
In the below example we even pass in a token to allow an application on the same machine to have multiple pools.
Example:
private string GetConnectionStringWithWorkStationId(string connectionString, string connectionPoolToken)
{
if (string.IsNullOrEmpty(machineName)) machineName = Environment.MachineName;
SqlConnectionStringBuilder cnbdlr;
try
{
cnbdlr = new SqlConnectionStringBuilder(connectionString);
}
catch
{
throw new ArgumentException("connection string was an invalid format");
}
cnbdlr.WorkstationID = machineName + connectionPoolToken;
return cnbdlr.ConnectionString;
}
Replace your above code.. by this.. and check again..
try
{
using (var connection = new SqlConnection(Utils.ConnectionString))
{
connection.Open();
using (var cmd = new SqlCommand("StoredProcedure", connection))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
var sqlParam = new SqlParameter("id_document", idDocument);
cmd.Parameters.Add(sqlParam);
int result = cmd.ExecuteNonQuery();
if (result != -1)
return "something";
//do something here
return "something else";
}
connection.Close();
connection.Dispose();
}
//do something
}
catch (SqlException ex)
{
return "something AKA didn't work";
}
Here's a reference:
http://msdn.microsoft.com/en-us/library/yh598w02(v=vs.80).aspx
What I know is that if you use an object within the using {} clause, that object inherits the IDisposable interface (i.e. SqlConnection inherits DbConnection, and DbConnection inherits IDisposable), which means if you get an exception, any object will be closed and disposed properly.
Here is the current architecture of my transaction scope source code. The third insert throws an .NET exception (Not a SQL Exception) and it is not rolling back the two previous insert statements. What I am doing wrong?
EDIT: I removed the try/catch from insert2 and insert3. I also removed the exception handling utility from the insert1 try/catch and put "throw ex". It still does not rollback the transaction.
EDIT 2: I added the try/catch back on the Insert3 method and just put a "throw" in the catch statement. It still does not rollback the transaction.
UPDATE:Based on the feedback I received, the "SqlHelper" class is using the SqlConnection object to establish a connection to the database, then creates a SqlCommand object, set the CommandType property to "StoredProcedure" and calls the ExecuteNonQuery method of the SqlCommand.
I also did not add Transaction Binding=Explicit Unbind to the current connection string. I will add that during my next test.
public void InsertStuff()
{
try
{
using(TransactionScope ts = new TransactionScope())
{
//perform insert 1
using(SqlHelper sh = new SqlHelper())
{
SqlParameter[] sp = { /* create parameters for first insert */ };
sh.Insert("MyInsert1", sp);
}
//perform insert 2
this.Insert2();
//perform insert 3 - breaks here!!!!!
this.Insert3();
ts.Complete();
}
}
catch(Exception ex)
{
throw ex;
}
}
public void Insert2()
{
//perform insert 2
using(SqlHelper sh = new SqlHelper())
{
SqlParameter[] sp = { /* create parameters for second insert */ };
sh.Insert("MyInsert2", sp);
}
}
public void Insert3()
{
//perform insert 3
using(SqlHelper sh = new SqlHelper())
{
SqlParameter[] sp = { /*create parameters for third insert */ };
sh.Insert("MyInsert3", sp);
}
}
I have also run into a similar issue. My problem occurred because the SqlConnection I used in my SqlCommands was already open before the TransactionScope was created, so it never got enlisted in the TransactionScope as a transaction.
Is it possible that the SqlHelper class is reusing an instance of SqlConnection that is open before you enter your TransactionScope block?
It looks like you are catching the exception in Insert3() so your code continues after the call. If you want it to rollback you'll need to let the exception bubble up to the try/catch block in the main routine so that the ts.Complete() statement never gets called.
An implicit rollback will only occur if the using is exited without calling ts.complete. Because you are handling the exception in Insert3() the exception never causes an the using statement to exit.
Either rethrow the exception or notify the caller that a rollback is needed (make change the signature of Insert3() to bool Insert3()?)
(based on the edited version that doesn't swallow exceptions)
How long do the operations take? If any of them are very long running, it is possible that the Transaction Binding bug feature has bitten you - i.e. the connection has become detached. Try adding Transaction Binding=Explicit Unbind to the connection string.
I dont see your helper class, but transaction scope rollsback if you don't call complete statement even if you get error from .NET code. I copied one example for you. You may be doing something wrong in debugging. This example has error in .net code and similar catch block as yours.
private static readonly string _connectionString = ConnectionString.GetDbConnection();
private const string inserttStr = #"INSERT INTO dbo.testTable (col1) VALUES(#test);";
/// <summary>
/// Execute command on DBMS.
/// </summary>
/// <param name="command">Command to execute.</param>
private void ExecuteNonQuery(IDbCommand command)
{
if (command == null)
throw new ArgumentNullException("Parameter 'command' can't be null!");
using (IDbConnection connection = new SqlConnection(_connectionString))
{
command.Connection = connection;
connection.Open();
command.ExecuteNonQuery();
}
}
public void FirstMethod()
{
IDbCommand command = new SqlCommand(inserttStr);
command.Parameters.Add(new SqlParameter("#test", "Hello1"));
ExecuteNonQuery(command);
}
public void SecondMethod()
{
IDbCommand command = new SqlCommand(inserttStr);
command.Parameters.Add(new SqlParameter("#test", "Hello2"));
ExecuteNonQuery(command);
}
public void ThirdMethodCauseNetException()
{
IDbCommand command = new SqlCommand(inserttStr);
command.Parameters.Add(new SqlParameter("#test", "Hello3"));
ExecuteNonQuery(command);
int a = 0;
int b = 1/a;
}
public void MainWrap()
{
TransactionOptions tso = new TransactionOptions();
tso.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
//TransactionScopeOption.Required, tso
try
{
using (TransactionScope sc = new TransactionScope())
{
FirstMethod();
SecondMethod();
ThirdMethodCauseNetException();
sc.Complete();
}
}
catch (Exception ex)
{
logger.ErrorException("eee ",ex);
}
}
If you want to debug your transactions, you can use this script to see locks and waiting status etc.
SELECT
request_session_id AS spid,
CASE transaction_isolation_level
WHEN 0 THEN 'Unspecified'
WHEN 1 THEN 'ReadUncomitted'
WHEN 2 THEN 'Readcomitted'
WHEN 3 THEN 'Repeatable'
WHEN 4 THEN 'Serializable'
WHEN 5 THEN 'Snapshot' END AS TRANSACTION_ISOLATION_LEVEL ,
resource_type AS restype,
resource_database_id AS dbid,
DB_NAME(resource_database_id) as DBNAME,
resource_description AS res,
resource_associated_entity_id AS resid,
CASE
when resource_type = 'OBJECT' then OBJECT_NAME( resource_associated_entity_id)
ELSE 'N/A'
END as ObjectName,
request_mode AS mode,
request_status AS status
FROM sys.dm_tran_locks l
left join sys.dm_exec_sessions s on l.request_session_id = s.session_id
where resource_database_id = 24
order by spid, restype, dbname;
You will see one SPID for two method calls before calling exception method.
Default isolation level is serializable.You can read more about locks and transactions here
I ran into a similar issue when I had a call to a WCF service operation in TransactionScope.
I noticed transaction flow was not allowed due to the 'TransactionFlow' attribute in the service interface. Therefore, the WCF service operation was not using the transaction used by the outer transaction scope. Changing it to allow transaction flow as shown below fixed my problem.
[TransactionFlow(TransactionFlowOption.NotAllowed)]
to
[TransactionFlow(TransactionFlowOption.Allowed)]