Using MySQLConnection in C# does not close properly - c#

I try to write a class to make MySql Connections easier. My problem is, after I open a connection and close it. It is still open in the Database and gets aborted.
I'm using the 'using' statement' of course, but the connection is still open and gets aborted after I exit the program.
Here's what my code looks like:
using (DatabaseManager db = new DatabaseManager())
{
using (MySqlDataReader result = db.DataReader("SELECT * FROM module WHERE Active=1 ORDER BY Sequence ASC"))
{
foreach (MySqlDataReader result in db.DataReader("SELECT * FROM module WHERE Active=1 ORDER BY Sequence ASC"))
{
//Do stuff here
}
}
}
The class Database manager opens the connection and closes it when disposed:
public DatabaseManager()
{
this.connectionString = new MySqlConnectionStringBuilder("Server=localhost;Database=businessplan;Uid=root;");
connect();
}
private bool connect()
{
bool returnValue = true;
connection = new MySqlConnection(connectionString.GetConnectionString(false));
connection.Open();
}
public void Dispose()
{
Dispose(true);
}
public void Dispose(bool disposing)
{
if (disposing)
{
if (connection.State == System.Data.ConnectionState.Open)
{
connection.Close();
connection.Dispose();
}
}
//GC.SuppressFinalize(this);//Updated
}
//Updated
//~DatabaseManager()
//{
// Dispose(false);
//}
So, I checked it in the debugger and the Dispose()-method is called and executes correctly.
What am I missing? Is there something I did wrong or misunderstood?
Just in case, the DataReader()-method (Updated version):
public IEnumerable<IDataReader> DataReader(String query)
{
using (MySqlCommand com = new MySqlCommand())
{
com.Connection = connection;
com.CommandText = query;
using (MySqlDataReader result = com.ExecuteReader(System.Data.CommandBehavior.CloseConnection))
{
while (result.Read())
{
yield return (IDataReader)result;
}
}
}
}
Ok, I tried to use the yield return:
foreach (MySqlDataReader result in db.DataReader("SELECT * FROM module WHERE Active=1 ORDER BY Sequence ASC"))
{
//...
}
And I changed the DataReader-method:
public IEnumerable<IDataReader> DataReader(String query)
{
using (MySqlCommand com = new MySqlCommand())
{
com.Connection = connection;
com.CommandText = query;
using (MySqlDataReader result = com.ExecuteReader())
{
while (result.Read())
{
yield return (IDataReader)result;
}
}
}
}
It works in the way that I can retrieve the data, yet I still have the same problem: The connection isn't closed properly.

Im unsure about mysqlconnection but the sql server counter part uses Connection pooling and does not close when you call close instead it puts it in the connection pool!
Edit: Make sure you dispose the Reader, Command, and Connection object!
Edit: Solved with the ConnectionString Parameter "Pooling=false" or the static methods MySqlConnection.ClearPool(connection) and MySqlConnection.ClearAllPools()

You need to wrap the Command and the DataReader in using statements as well.

According to the mysql docs, the MySQLConnection is not closed when it goes out of scope. Therefore you must not use it inside a using.
Quote...
"If the MySqlConnection goes out of scope, it is not closed. Therefore, you must explicitly close the connection by calling MySqlConnection.Close or MySqlConnection.Dispose."

Have a look at using something like this:
private static IEnumerable<IDataRecord> SqlRetrieve(
string ConnectionString,
string StoredProcName,
Action<SqlCommand> AddParameters)
{
using (var cn = new SqlConnection(ConnectionString))
using (var cmd = new SqlCommand(StoredProcName, cn))
{
cn.Open();
cmd.CommandType = CommandType.StoredProcedure;
if (AddParameters != null)
{
AddParameters(cmd);
}
using (var rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
while (rdr.Read())
yield return rdr;
}
}
}

Related

Invalid Attempt To Call Read when reader is closed .NET

I currently have a DB library used for database access and I use it across several projects. I currently use the following code to get a recordset.
METHODS
public static IDataReader GetRs(string sql)
{
using (var con = NewSqlConnection())
{
con.Open();
return GetRs(sql, con);
}
}
public static IDataReader GetRs(string sql, SqlConnection dbconn)
{
using (var cmd = new SqlCommand(sql, dbconn))
{
int tries = 1;
while (tries <= 3)
{
try
{
if (dbconn.State == ConnectionState.Closed)
{
dbconn.Open();
}
DataTable myTable = new DataTable();
var reader = cmd.ExecuteReader();
myTable.Load(reader);
return myTable.CreateDataReader();
//return cmd.ExecuteReader();
}
catch (SqlException ex)
{
if (ex.Message.Contains("Timeout expired") || ex.Number == 1205) // Deadlock
{
Thread.Sleep(1000);
if (tries == 3)
{
throw ex;
}
tries += 1;
cmd.CommandTimeout *= 10;
}
else
{
throw ex;
}
}
}
}
throw new Exception("Could not get RecordSet");
}
USAGE
public static void Test()
{
using(var reader = GetRs("SELECT Col FROM TABLE"))
{
while(reader.Read())
{
// do stuff with data here e.g. var value = reader[0];
}
}
}
While this method works, as you can see it loads the entire dataset into memory thus causing issues with scaling.
I tried replacing the following code in the GetRs(string sql, SqlConnection con) method
DataTable myTable = new DataTable();
var reader = cmd.ExecuteReader();
myTable.Load(reader);
return myTable.CreateDataReader();
and tried returning just the return cmd.ExecuteReader();
However an error is thrown on the while (reader.Read()) - Invalid attempt to call read when the reader is closed. I am guessing this is because the SqlConnection property is disposed (and hence closed) after returning the IDataReader.
I'm aware that I can wrap the GetRs method with a new sql connection but this means rewriting a lot of my code, and I was hoping that I would be able to dispose the reader AND the connection with my using(var reader = GetRs()) method.
Is there any way I can still use these methods without loading the whole dataset into memory?
You can inject in your code with a Action<DataTableReader> parameter to you GetRs call.
Try this:
public static void GetRs(string sql, Action<DataTableReader> consumer)
{
using (var con = NewSqlConnection())
{
con.Open();
GetRs(sql, con, consumer);
}
}
public static void GetRs(string sql, SqlConnection dbconn, Action<DataTableReader> consumer)
{
using (var cmd = new SqlCommand(sql, dbconn))
{
if (dbconn.State == ConnectionState.Closed)
{
dbconn.Open();
}
DataTable myTable = new DataTable();
var reader = cmd.ExecuteReader();
myTable.Load(reader);
consumer(myTable.CreateDataReader());
}
}
(I removed your try/catch code for clarity.)
Then you call it like this:
public static void Test()
{
GetRs("SELECT Col FROM TABLE", reader =>
{
while(reader.Read())
{
// do stuff with data here e.g. var value = reader[0];
}
});
}
Your connection object was closed by the following line
using (var con = NewSqlConnection())
{
con.Open();
return GetRs(sql, con);
}//Connection object gets released here
But still, you are returning the reader object which was created by SqlCommand object using the above connection object. Hence the reader object is closed.

C# Mysql Connection must be valid and open

First of all: I got my code running without using oop. I declared all my variables inside the same class and opened/closed the connection right before and after passing the query to the db. That worked! Now with some new experiences I tried to split my code into different classes. Now it wont work anymore.
It tells me "Connection must be valid and open". Enough text, here's my current code:
Services.cs
public static MySqlConnection conn // Returns the connection itself
{
get
{
MySqlConnection conn = new MySqlConnection(Services.ServerConnection);
return conn;
}
}
public static string ServerConnection // Returns the connectin-string
{
get
{
return String.Format("Server={0};Port=XXXX;Database=xxx;Uid=xxx;password=xxXxxXxXxxXxxXX;", key);
}
}
public static void DB_Select(string s, params List<string>[] lists)
{
try
{
MySqlCommand cmd = conn.CreateCommand();
cmd.CommandType = CommandType.Text;
string command = s;
cmd.CommandText = command;
MySqlDataReader sqlreader = cmd.ExecuteReader();
while (sqlreader.Read())
{
if (sqlreader[0].ToString().Length > 0)
{
for (int i = 0; i < lists.Count(); i++)
{
lists[i].Add(sqlreader[i].ToString());
}
}
else
{
foreach (List<string> save in lists)
{
save.Add("/");
}
}
}
sqlreader.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error while selecting data from database!\nDetails: " + ex);
}
}
LoginForm.cs
private void checkUser(string username, string password)
{
using (Services.conn)
{
Services.conn.Open();
Services.DB_Select("..a short select statement..");
Services.conn.Close();
}
I guess this is all we need. I have shortened my code to get a focus on the problem.
I created Services.cs to get a global way to access the db from all forms without copy&pasting the connection info. Now when I reach my LoginForm.cs it throws an error "Connection must be valid and open". I've already debugged my code. It's all time closed. Even when passing conn.Open() it stays closed. Why?
Another try: I've also tried placing conn.Open() and conn.Close() inside Services.DB_Select(..) at the beginning and end. Same error here.
I have to say: The code worked before and I've used the same connection-string. So the string itself is surely valid.
I appreciate any help given here!
The problem is that you don't store the connection that was returned from your factory property. But don't use a property like a method. Instead use it in this way:
using (var con = Services.conn)
{
Services.conn.Open();
Services.DB_Select("..a short select statement..", con ));
//Services.conn.Close(); unnecessary with using
}
So use the same connection in the using that was returned from the property(or better created in the using) and pass it to the method which uses it. By the way, using a property as factory method is not best practise.
But in my opinion it's much better to create the connection where you use it, best place is in the using statement. And throw the con property to the garbage can, it is pointless and a source for nasty errors.
public static void DB_Select(string s, params List<string>[] lists)
{
try
{
using(var conn = new MySqlConnection(Services.ServerConnection))
{
conn.Open();
MySqlCommand cmd = conn.CreateCommand();
cmd.CommandText = s;
using( var sqlreader = cmd.ExecuteReader())
while (sqlreader.Read())
{
if (sqlreader[0].ToString().Length > 0)
{
for (int i = 0; i < lists.Count(); i++)
{
lists[i].Add(sqlreader[i].ToString());
}
}
else
{
foreach (List<string> save in lists)
{
save.Add("/");
}
}
} // unnecessary to close the connection
} // or the reader with the using-stetement
}
catch (Exception ex)
{
MessageBox.Show("Error while selecting data from database!\nDetails: " + ex);
}
}
Try to restructure your Services class as follows
public static MySqlConnection conn // Returns the connection itself
{
get
{
MySqlConnection conn = new MySqlConnection(Services.ServerConnection);
return conn;
}
}
private static string ServerConnection // Returns the connectin-string - PRIVATE [Improved security]
{
get
{
return String.Format("Server={0};Port=XXXX;Database=xxx;Uid=xxx;password=xxXxxXxXxxXxxXX;", key);
}
}
// Rather than executing result here, return the result to LoginForm - Future improvement
public static void DB_Select(MySqlConnection conn ,string s, params List<string>[] lists)
{
try
{
MySqlCommand cmd = conn.CreateCommand();
cmd.CommandType = CommandType.Text;
string command = s;
cmd.CommandText = command;
MySqlDataReader sqlreader = cmd.ExecuteReader();
while (sqlreader.Read())
{
if (sqlreader[0].ToString().Length > 0)
{
for (int i = 0; i < lists.Count(); i++)
{
lists[i].Add(sqlreader[i].ToString());
}
}
else
{
foreach (List<string> save in lists)
{
save.Add("/");
}
}
}
sqlreader.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error while selecting data from database!\nDetails: " + ex);
}
}
In LoginForm.cs use returning connection and store it there. When you need to execute query, use
MySqlConnection conn=Services.conn(); // Get a new connection
Services.DB_Select(conn,"..a short select statement.."); // Executing requirement
Services.conn.Close();
Additional - I suggest you need to return MySqlDataReader to LoginForm and handle results there
private MySqlConnection _conn;
public MySqlConnection conn // Returns the connection itself
{
get
{
if(_conn == null)
_conn = new MySqlConnection(Services.ServerConnection);
return _conn;
}
}

How do i use SqlTransaction across multipe methods

Let's assume we have an Object A which can be deleted an Object B which hold a forendkey from A
If you want to deleted A, you have to delete the forendkey from B first and then you can delete A but if something goes wrong it should be rolled back but i also want to use the delete forendkey from B independent but at the moment i don't know how to achieve this
my current idea :
public void DeleteA(Object a)
{
using (SqlConnection con = new SqlConnection())
{
con.open();
using (SqlTransaction tr = con.BeginTransaction())
{
try
{
DeleteAfromAllB(a, con, tr);
using (SqlCommand cmd = new SqlCommand("STP_A_Delete", con))
{
cmd.Transaction = tr;
// some parameters
// some sort of Execute
// e.g.: cmd.ExecuteNonQuery();
}
tr.Commit();
}
catch (SqlException ex)
{
//ExceptionHandling
}
}
}
}
private void DeleteAfromAllB(Object a, SqlConnection con, SqlTransaction tr)
{
try
{
using (SqlCommand cmd = new SqlCommand("STP_B_Delete_Referenc_To_A", con))
{
cmd.Transaction = tr;
// some parameters
// some sort of Execute
// e.g.: cmd.ExecuteNonQuery();
}
}
catch (SqlException ex)
{
//ExceptionHandling
}
}
public void DeleteAfromAllB(Object a)
{
using (SqlConnection con = new SqlConnection())
{
con.open();
using (SqlTransaction tr = con.BeginTransaction())
{
DeleteAfromAllB(a,con,tr);
tr.Commit();
}
}
}
but like you can see this is pretty ugly
The call
public void DeleteAfromAllB(Object a)
does not need to pass the SqlConnection as you can reference from tr.Connection. So you just need the SqlTransaction as parameter. So for your original question, yes I think passing in the SqlTransaction is the way to go. Personally I prefer this way because you can easily trace the call stack / scope of the transaction (i.e. where the transaction started/finished).
Another alternative is to use a TransactionScope.
E.g.
private void DeleteAfromAllB(Object a)
{
using (var con = new SqlConnection())
{
con.open();
using (var cmd = new SqlCommand("STP_B_Delete_Referenc_To_A", con))
{
// some parameters
// some sort of Execute
// e.g.: cmd.ExecuteNonQuery();
}
}
}
public void DeleteAfromAllB_TopLevel(Object a)
{
using (var scope = new TransactionScope())
{
try
{
DeleteAfromAllB(a);
// The Complete method commits the transaction. If an exception has been thrown,
// Complete is not called and the transaction is rolled back.
scope.Complete();
}
catch (Exception)
{
//ExceptionHandling
}
}
}

How to copy MySqlDataReader into an array and then loop throught the array?

I am new to C# so yes this should be a faily easy question but I can't seem to find the answer to it.
I have a method that query a database.
What I am trying to do here is handle the loop though the data outside the method.
public MySqlDataReader getDataSet(string query)
{
MySqlDataReader dataset = null;
MySqlConnection conn = new MySqlConnection(conn_string);
if (startConnection(conn) == true)
{
MySqlCommand cmd = new MySqlCommand(query, conn);
dataset = cmd.ExecuteReader();
closeConnection(conn);
}
return dataset;
}
what I could do is write a while loop just before the closeConnection(conn); line and handle the data. But, I don't want to do it inside this method and I want to do it somewhere else in my code.
In one of my forms I want to read the database on the load so here is what I tried to do
public newDepartment()
{
InitializeComponent();
inputDepartmentName.Text = "Hi";
dbConnetion db = new dbConnetion();
MySqlDataReader ds = db.getDataSet("SELECT name FROM test;");
while (ds.Read())
{
//Do Something
}
}
The problem that I am having is that I get an error Invalid attempt to Read when reader is closed
Which I belive I get this issue because I close the connection and then I am trying to read it. so What I need to do is read the data from the query and put it in an array and then loop through the array and deal with the data in a different form.
How can I workaround this issue? if my idea is good then how can I copy the data into an array and how do I loop though the array?
Here is the full class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MySql.Data.MySqlClient;
using System.Windows.Forms;
namespace POS
{
public class dbConnetion
{
//private OdbcConnection conn;
private readonly string mServer;
private readonly string mDatabase;
private readonly string mUid;
private readonly string mPassword;
private readonly string mPort;
private readonly string conn_string;
public dbConnetion()
{
mServer = "localhost";
mDatabase = "pos";
mUid = "root";
mPassword = "";
mPort = "3306";
conn_string = String.Format("server={0};user={1};database={2};port={3};password={4};", mServer, mUid, mDatabase, mPort, mPassword);
}
//Start connection to database
private bool startConnection(MySqlConnection mConnection)
{
try
{
mConnection.Open();
return true;
}
catch (MySqlException ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK);
return false;
}
}
//Close connection
private bool closeConnection(MySqlConnection mConnection)
{
try
{
mConnection.Close();
return true;
}
catch (MySqlException ex)
{
MessageBox.Show(ex.Message);
return false;
}
}
public MySqlDataReader getDataSet(string query)
{
MySqlDataReader dataset = null;
MySqlConnection conn = new MySqlConnection(conn_string);
if (startConnection(conn) == true)
{
MySqlCommand cmd = new MySqlCommand(query, conn);
dataset = cmd.ExecuteReader();
closeConnection(conn);
}
return dataset;
}
public void processQuery(string strSQL, List<MySqlParameter> pars)
{
MySqlConnection conn = new MySqlConnection(conn_string);
if (startConnection(conn) == true)
{
MySqlCommand cmd = new MySqlCommand(strSQL, conn);
foreach (MySqlParameter param in pars)
{
cmd.Parameters.Add(param);
}
cmd.ExecuteNonQuery();
closeConnection(conn);
}
}
}
}
Putting the records into an array would destroy the best feature of a using a datareader: that you only need to allocate memory for one record at a time. Try doing something like this:
public IEnumerable<T> getData<T>(string query, Func<IDataRecord, T> transform)
{
using (var conn = new MySqlConnection(conn_string))
using (var cmd = new MySqlCommand(query, conn))
{
conn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
yield return transform(rdr);
}
}
}
}
While I'm here, there's a very serious security flaw with this code and the original. A method like this that only accepts a query string, with no separate mechanism for parameters, forces you to write code that will be horribly horribly vulnerable to sql injection attacks. The processQuery() method already accounts for this, so let's extend getDataset() to avoid that security issue as well:
public IEnumerable<T> getData<T>(string query, List<MySqlParameter> pars, Func<IDataRecord, T> transform)
{
using (var conn = new MySqlConnection(conn_string))
using (var cmd = new MySqlCommand(query, conn))
{
if (pars != null)
{
foreach(MySqlParameter p in pars) cmd.Parameters.Add(p);
}
conn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
yield return transform(rdr);
}
}
}
}
Much better. Now we don't have to write code that's just asking to get hacked anymore. Here's how your newDepartment() method will look now:
public newDepartment()
{
InitializeComponent();
inputDepartmentName.Text = "Hi";
dbConnetion db = new dbConnetion();
foreach(string name in db.getDataSet("SELECT name FROM test;", null, r => r["name"].ToString() ))
{
//Do Something
}
}
One thing about this code is that is uses a delegate to have you provide a method to create a strongly-typed object. It does this because of the way the datareaders work: if you don't create a new object at each iteration, you're working on the same object, which can have undesirable results. In this case, I don't know what kind of object you're working with, so I just used a string based on what your SELECT query was doing.
Based on a separate discussion, here's an example of calling this for a more complicated result set:
foreach(var item in db.getDataSet(" long query here ", null, r =>
new columnClass()
{
firstname = r["firstname"].ToString(),
lastname = r["lastname"].ToString(),
//...
}
) )
{
//Do something
}
Since you are new to .Net I thought I point out that there are two layers of database access in ADO.Net. There are the data reader way that you are using and all of that is online only forward reading of queries. This is the lowest level access and will give you the best performance but it is more work. For most connection types you can only execute one command or have one active data reader per connection (And you can't close the connection before you have read the query as you are doing).
The other form is the offline data adapter and requires just a little bit different code, but is generally easier to use.
public DataTable getDataSet(string query)
{
MySqlConnection conn = new MySqlConnection(conn_string);
if (startConnection(conn) == true)
{
MySqlDataAdapter adapter = new MySqlDataAdapter(query, conn);
DataTable table = new DataTable();
adapter.Fill(table);
closeConnection(conn);
return table;
}
return null;
}
This will result in you getting a DataTable with columns and rows corresponding to the result of your query (Also look into command builders if you want to post changes back to the database later on from it, but for that you will need to keep the connection open).
One nice thing with using the data adapter is that it will figure out what the correct data types should be so you don't have to worry about invalid cast exceptions while reading the data from the data reader.
As somebody pointed out though you will need to read all the data into memory which could be a problem if you are dealing with a lot of memory. Also the DataTable class is really slow when you start dealing with a lot of records. Finally DataTable and DataSet classes also generally hook well into UI components in .Net so that their contents can easily be displayed to users.

System.ObjectDisposedException: Cannot access a disposed object. Object name: 'OracleConnection' Reuse of OracleConnection object

I would to reuse a OracleConnection object for more queries
so I wrote a simple class:
public static class DbConnectionsManager
{
/// <summary>
///
/// </summary>
private static OracleConnection _dbConnection = null;
/// <summary>
///
/// </summary>
/// <param name="aConnectionString"></param>
/// <returns></returns>
public static OracleConnection GetDatabaseConnection(string aConnectionString)
{
try
{
if (_dbConnection == null)
{
_dbConnection = new OracleConnection(aConnectionString);
_dbConnection.Open();
return _dbConnection;
}
if (_dbConnection.State == System.Data.ConnectionState.Closed)
{
_dbConnection.ConnectionString = aConnectionString;
_dbConnection.Open();
return _dbConnection;
}
if (!_dbConnection.ConnectionString.Equals(aConnectionString))
{
_dbConnection.Close();
_dbConnection.ConnectionString = aConnectionString;
_dbConnection.Open();
return _dbConnection;
}
return null;
}
catch (Exception e)
{
return null;
}
}
}
in this way I can use the connection several times:
using (OracleConnection connection =
DbConnectionsManager.GetDatabaseConnection(aDbConnectionString))
{
OracleCommand command = connection.CreateCommand();
string sql = "SELECT * FROM MYTABLE";
command.CommandText = sql;
OracleDataReader reader = command.ExecuteReader();
while (reader.Read())
{
string myField = (string)reader["EXAMPLE"];
Console.WriteLine(myField);
}
}
When I call the method the first time everything works fine.
If I recall the method the static object is != null but the connection result closed!
I never close the connection!
When you try to reopen the connection I have this exception
....
if (_dbConnection.State == System.Data.ConnectionState.Closed)
{
_dbConnection.ConnectionString = aConnectionString;
_dbConnection.Open();
return _dbConnection;
}
...
Error
Message = "Cannot access a disposed object.\r\nObject name: 'OracleConnection'."
As the error says it's a disposed object. That means you need to remove the using ( clause; this clause disposed your connection object and that's why you cannot use this object outside using (. That means you need to create a new object of class if you want to use it outside using (.
See: C# Using Statement
As the other answer explains, the using clause isn't a natural fit for something you're wanting to re-use. However I had the same idea - I would still like to use this type of pattern to auto-open & auto-close the connection. If that is what you were hoping to do, then the key is that the object you are "using" cannot be the OracleConnection itself since you do want to reuse this, and "using" it will kill it. You really just want a new object which opens the connection on creation, and closes the connection on Dispose, and nothing more. This should do the trick (I am using it for my own purposes):
internal class OpenedContext : IDisposable
{
private OracleConnection _connection;
public OpenedContext(OracleConnection conn) {
_connection = conn;
if (_connection.State != System.Data.ConnectionState.Open) _connection.Open();
}
public void Dispose() {
if (_connection.State != System.Data.ConnectionState.Closed) _connection.Close();
}
}
And then in your example you could do something like...
// Early on...
OracleConnection _connection = DbConnectionsManager.GetDatabaseConnection(aDbConnectionString);
// ... Later on, in various other calls ...
using (new OpenedContext(_connection))
{
OracleCommand command = _connection.CreateCommand();
string sql = "SELECT * FROM MYTABLE";
command.CommandText = sql;
OracleDataReader reader = command.ExecuteReader();
while (reader.Read())
{
string myField = (string)reader["EXAMPLE"];
Console.WriteLine(myField);
}
}
Although it's goofy in a way, I definitely prefer setting up a pattern like this to always expecting future coders to manually "wrap" the re-open & re-close around every database call. It's more readable (or ignorable) and less risky (if you're greatly concerned about leaving the connection closed while idle).
public OracleConnection GetConnection()
{
//Romove using(...) clouse
using (OracleConnection conn = new OracleConnection(_connSettings.GetConnectionString()))
{return con;}
The issue is inner using() clause problem.
using (OracleConnection con = GetConnection()){}

Categories