.NET and MySQL. DataReader and NonQuery connection exceptions - c#

I have created two different connections. One for DataReader and second for NonQuery.
DataReaderConnect = DBUtils.GetDBConnection(_DbUserName, _DbPassword);
DataReaderConnect.Open();
NonQueryConnect = DBUtils.GetDBConnection(_DbUserName, _DbPassword);
NonQueryConnect.Open();
I have 2 methods that is using those connections
public async Task UpdateUser(cUser.UserObject User)
{
string SQL = $"UPDATE {_DbName}.dbUserTable SET Name = #Name....";
using MySqlCommand Command = new MySqlCommand(SQL, NonQueryConnect);
Command.Parameters.AddWithValue("#Name", User.Name);
...
await Command.ExecuteNonQueryAsync().ConfigureAwait(false);
}
public async Task<cUser.UserObject> GetUser(int ttvUserID)
{
List<cUser.UserObject> Users = new List<cUser.UserObject>();
string SQL = $"SELECT * FROM {_DbName}.dbUserTable WHERE TwitchID = #ID";
using MySqlCommand Command = new MySqlCommand(SQL, DataReaderConnect);
Command.Parameters.AddWithValue("#ID", ttvUserID);
using var sqlReader = await Command.ExecuteReaderAsync().ConfigureAwait(false);
while (await sqlReader.ReadAsync().ConfigureAwait(false))
{
Users.Add(new cUser.UserObject()
{
dbID = Convert.ToInt32(sqlReader[0]),
TwitchID = Convert.ToInt32(sqlReader[1]),
Name = sqlReader[2].ToString(),
isSub = Convert.ToInt32(sqlReader[3]),
isVip = Convert.ToInt32(sqlReader[4])
});
}
}
But im getting exception "There is already an open DataReader associated with this Connection which must be closed first." And I cant figure out why. Im not using DataReader for NonQuery and vice versa
edit: I figured it out and it was just me being silly and apparently I can't read. .NET not only cant support DataReader and NonQuery on the same connection but also more then one DataReader on the same connection.

Don't keep multiple open connections to the database. In fact, don't keep open connections at all to the database. It leaves hanging resources that the underlying system is better equipped to manage than our code is.
These interactions with the database should:
Open the connection
Use the connection
Close the connection
So instead of this:
public async Task UpdateUser(cUser.UserObject User)
{
string SQL = $"UPDATE {_DbName}.dbUserTable SET Name = #Name....";
using MySqlCommand Command = new MySqlCommand(SQL, NonQueryConnect);
Command.Parameters.AddWithValue("#Name", User.Name);
//...
await Command.ExecuteNonQueryAsync().ConfigureAwait(false);
}
Instead, use something like this (other issues notwithstanding):
public async Task UpdateUser(cUser.UserObject User)
{
using var connection = DBUtils.GetDBConnection(_DbUserName, _DbPassword);
var sql = $"UPDATE {_DbName}.dbUserTable SET Name = #Name....";
using var command = new MySqlCommand(sql, connection);
command.Parameters.AddWithValue("#Name", User.Name);
//...
await command.ExecuteNonQueryAsync().ConfigureAwait(false);
}
This keeps the scope of the connection itself limited to just this operation, since both command and connection will be disposed at the end of their respective using blocks. That way you don't have open connections hanging around that aren't being used, and you don't run into errors like the one you're seeing now.

Related

Async Call to Snowflake from .Net

Is there a way to call a Snowflake Stored Procedure asynchronously from .Net? I am running a .Net Core API App using Snowflake’s DotNet driver. Basically I need to be able to use conn.OpenAsync and cmd.ExecuteNonQueryAsync like you normally would with a SQL Server, but that seems to be impossible.
executeResults.SessionId =RunNonQueryAsync(connectionString, command).Result;
static async Task<String> RunNonQueryAsync(string execConnection, string execCommand)
{
String sessionId = null;
using (IDbConnection conn = new SnowflakeDbConnection())
{
conn.ConnectionString = execConnection;
conn.Open();
// Get Session Information
IDbCommand cmd2 = conn.CreateCommand();
cmd2.CommandText = "SELECT CURRENT_SESSION() as SESSION_ID";
IDataReader rdr2 = cmd2.ExecuteReader();
while (rdr2.Read())
{
sessionId = rdr2.GetValue(0).ToString();
}
Task taskA = new Task(() =>
{
using (IDbConnection cn = new SnowflakeDbConnection())
{
cn.ConnectionString = execConnection;
cn.Open();
IDbCommand cm = conn.CreateCommand();
cm.CommandText = "call StoredProcedureThatTakes5MinutesToRun";
cn.Close();
}
});
taskA.Start();
}
return sessionId;
}
My browser sends an HTTP GET which the DotNet API receives along with the properties [SQL Type: "NonQuery" AND StatementText:"Call StoredProcX"].
That sounds like a absolute security nightmare. SQL Injections are bad enough, but that is basically putting a Central Venous Catheter into your Database.
At best, I would allow some simple Enumeration, integer or string value to be send with the request. One that is then passed through a switch/case statement. Maybe fed into a Dictionary<String, Action>, if that works better. The case or the action will then do the work of sending off the requests.
From there it is really just doing two things within then same connection:
using (IDbConnection cn = new SnowflakeDbConnection())
{
cn.Open();
//Build command 1
//Execute command 1
//Do stuff with results of command 1
//Build command 2
//Execute command 2
//Do stuff with results of command 2
//Can propably be omited, as Dispose usually includes close. But we are talking about some 3rd party code, that might not be tested that well
cn.Close();
}

C# Using multiple MySQL connection or put queries in queue to execute respectively

I have a client/server app and my server stores data in a MySQL database, currently I have made a connection and I do queries without queue or something. I don't think this is a good solution for this, because when a MySQLDataReader opens another one can't be execute at the same time and first one must be closed. I think I have two options, either make a connection by every DataReader or put my queries in a queue to execute them one by one.
I want to know which one is the best or is there any way or something to prevent errors and exception which causes this error
There is already an open DataReader associated with this Connection which must be closed first.
This is how currently I am doing queries. I first get the main connection and do queries. it my causes above error.
string query = "SELECT * FROM users WHERE username = #username";
ServerModel.Database.CheckConnection(); // Get the main connection
MySqlCommand cmd = new MySqlCommand(query, ServerModel.Database);
cmd.Parameters.AddWithValue("#username", username);
UserStatus userStatus;
using (MySqlDataReader dataReader = cmd.ExecuteReader())
{
if (dataReader.Read())
{
...
dataReader.Close();
return userStatus;
}
}
To note that this server may do thousands of queries at moment. think about a chat server.
In this case please don't use the using block, I hope below approach will work fine.
string query = "SELECT * FROM users WHERE username = #username";
ServerModel.Database.CheckConnection(); // Get the main connection
MySqlCommand cmd = new MySqlCommand(query, ServerModel.Database);
cmd.Parameters.AddWithValue("#username", username);
UserStatus userStatus;
MySqlDataReader dataReader = cmd.ExecuteReader()
if (dataReader.Read())
{
...
dataReader.Close();
return userStatus;
}

C# :error There is already an open DataReader associated with this Command which must be closed first

I used to call several functions with this connection string:
class Solders_DB
{
SqlConnection connection;
SqlCommand query;
String command;
public Solders_DB()
{
connection = new SqlConnection();
connection.ConnectionString = "Server=localhost;Database=Scheduling_Employee ;Trusted_Connection=True;MultipleActiveResultSets=True;";
query = new SqlCommand();
}
As you see I used this MultipleActiveResultSets=True; in my connection but in this function :
command = #"SELECT [GID] FROM [Scheduling_Employee].[dbo].[Solder] where [ID]=#ID";
query.CommandText = command;
query.Parameters.Clear();
query.Parameters.AddWithValue("#ID", id);
Object o= query.ExecuteScalar();
I faced with this error:
There is already an open datareader associated with this command which must be closed first
The code in your question is not complete. Please explain yourself better for further assistance.
When using a SqlConnection is recommended to use the 'using' statement like this:
using (SqlConnection SqlConn = new SqlConnection(ConnString))
{
try
{
SqlConn.Open();
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
return null;
}
}
You're trying to read data from the first execution of your query, please update your question and put the complete code and the error stack.
Its usually when there was another query before that one and has not yet stopped executing inside the database engine (maybe it was a heavy script). You could try query = new SqlCommand();, query.Cancel() or
while(query.Connection.State != System.Data.ConnectionState.Open){ Threading.Sleep(100);}

Mysql/C# error: There is already an open DataReader associated with this Connection which must be closed first

I have a piece of code to execute a mysqlcommand but sometimes it throws the exception
There is already an open DataReader associated with this Connection which must be closed first
It is unpredictable when the exception will be throw sometimes my program works fine for over 15min but then it craches. I looked online for an answer but i didn't find anything.
This is my code:
public static List<string> DoCommand(string commandLine, string myConString) {
MySqlCommand command = new MySqlCommand(commandLine);
List<string> tables = new List<string>();
try {
using (MySqlConnection mySqlConnection = new MySqlConnection(myConString)) {
mySqlConnection.Open();
command.Connection = mySqlConnection;
command.BeginExecuteReader();
using (MySqlDataReader SqlDR = command.ExecuteReader()) {
while (SqlDR.Read()) { //Async reading, wait untill reading is done
tables.Add(SqlDR.GetString(0));
}
//SqlDR.Close();
//SqlDR.Dispose();
}
//mySqlConnection.Close();
//mySqlConnection.Dispose();
}
} catch (Exception exp) { } finally {
command.Connection = null;
}
//System.Threading.Thread.Sleep(50); //Give connection time to flush
return tables;
}
there are some of the solutions that didn't work in comment.
This is the only code taht connects to mysql so i am sure all connections are closed
If you above code is what you intended, this line is not required:
command.BeginExecuteReader();
The SqlCommand.BeginExecuteReader Method:
Initiates the asynchronous execution
of the Transact-SQL statement or
stored procedure that is described by
this SqlCommand, and retrieves one or
more result sets from the server
Replace
using (MySqlDataReader SqlDR = command.ExecuteReader())
with
using (MySqlDataReader SqlDR = command.EndExecuteReader()

C# MySqlConnection won't close

I have an application that fires a mysql command (query) "show databases", the query works and returns properly but I can't close my connections. The user I used had 24 connections allowed at the same time so the problem popped up further down my program but reducing the allowed connections to 2 shows me that I can't even close the first query (which isn't in a loop). The code is the following:
protected override Dictionary<string, Jerow_class_generator.Database> loadDatabases()
{
MySqlConnection sqlCon = new MySqlConnection(this.ConnectionString);
sqlCon.Open();
MySqlCommand sqlCom = new MySqlCommand();
sqlCom.Connection = sqlCon;
sqlCom.CommandType = CommandType.Text;
sqlCom.CommandText = "show databases;";
MySqlDataReader sqlDR;
sqlDR = sqlCom.ExecuteReader();
Dictionary<string, Jerow_class_generator.Database> databases = new Dictionary<string, Jerow_class_generator.Database>();
string[] systemDatabases = new string[] { "information_schema", "mysql" };
while (sqlDR.Read())
{
string dbName = sqlDR.GetString(0);
if (!systemDatabases.Contains(dbName))
{
databases.Add(sqlDR.GetString(0), new MySQL.Database(dbName, this));
}
}
sqlCom.Dispose();
sqlDR.Close();
sqlCon.Close();
sqlCon.Dispose();
return databases;
}
P.S. The 'New MySQL.Database(dbName, this));' is my owm made class which only stores the DB structure, could be considered irrelevant.
The exact error I get is 'max_user_connections'. on the connection.open line of the next time a query needs to be fired.
Rather than keeping track of all the Open/Close/Dispose calls all over the place, I'd recommend just replacing all of those with using statements. This will make sure the expected scope of each object is clear and that it will be destroyed/disposed upon exiting that scope.
Close() nor using will help alone with your problem because ADO.NET is using its own connection pooling and connections are by default not closed until program is closed. There are few options to solve this, but consider performance implications and is this really desired behavior for your application.
Add ";Pooling=False" to your connection string.
SqlConnection.ClearPool Method
SqlConnection.ClearAllPools Method
For more information read: SQL Server Connection Pooling (ADO.NET)
Along with the using suggestions above, when creating your sqlDR variable you should use the CloseConnection command behavior to close the actual connection if that is your intended action. As noted in the documentation here.
When the command is executed, the associated Connection object is closed when the associated DataReader object is closed.
So your code to instantiate your reader would look like this:
//to instantiate your variable
MySqlDataReader sqlDR;
sqlDR = sqlCom.ExecuteReader(CommandBehavior.CloseConnection);
//closing your datareader reference here will close the connection as well
sqlDR.Close();
If you wrap all your code in a using block using the above method, you don't need any of those Close() or Dispose() methods other than the sqlDR.Close();
when use "using" key word what happen is.when the garbage collector activate it first dispose objects which was declred in using statement.
I recommend using connection pooling in combination with the MySqlHelper class, passing the connection string as the first argument. That allows MySQL to open the connection if necessary, or keep it open according to the pooling cfg, without you having to know about it.
I changed my code to use 1 connection and keep it open and when testing I came across an error that a datareader should be closed. Now since all my queries didn't close the dataReader object (I used dataTable.Load(cmd.ExecuteReader()).) I think the problem might be there.
Keeping 1 open connection worked perfectly so I don't know what caused the not closing problem. I gues it was the dataReader not closing by itself.
Close() will definitely help you close your.
using (MySqlConnection conn = GetConnection())
{
conn.Open();
using (MySqlCommand cmd = conn.CreateCommand())
{
if (conn.State != ConnectionState.Open)
{
conn.Open();
}
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "UserDetail";
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
list.Add(new Album()
{
Id = Convert.ToInt32(reader["UId"]),
Name = reader["FirstName"].ToString(),
ArtistName = reader["LastName"].ToString()
});
}
}
}
}
In the above code, you can see one if condition before opening the connection it will help you to reuse your already open connections check below code.
if (conn.State != ConnectionState.Open)
{
conn.Open();
}

Categories

Resources