open/close an oracle connection multiple times c# - c#

In my business logic, I use multiple oracle query's multiple times. What is the best way to open and close the oracle connection?
private void update()
{
OracleConnection con = new OracleConnection("Connection Statement");
OracleCommand command = new OracleCommand("Select Statement");
con.Open();
OracleDataReader reader = command.ExecuteReader();
reader.Close();
con.Close();
// A for loop
con.Open();
command = new OracleCommand("Update statement");
command.ExecuteNonQuery();
con.Close();
con.Open();
command = new OracleCommand("Second Update statement");
command.ExecuteNonQuery();
con.Close();
}
My code looks like this. Should I open and close my oracle connection for every command or open before the first command and close after the last command.
P.S. This update function is called over 100 times in my application.

As the connection is local to the method create it in a using block and then use it as many times as you need to in that block. The block can contain loops or whatever, there is no rule that states you have to discard the connection after you use it once.
However connection sharing is discouraged, so do not create a class level instance or static instance for shared use.
private void update()
{
using(OracleConnection con = new OracleConnection("Connection Statement"))
{
con.Open();
using(var command = new OracleCommand("Select Statement", con))
using(OracleDataReader reader = command.ExecuteReader()}
{
}
// A for loop
using(var command = new OracleCommand("Update statement", con))
{
command.ExecuteNonQuery();
}
using(var command = new OracleCommand("Second Update statement", con))
{
command.ExecuteNonQuery();
}
}
}

Related

Having trouble with: The connection was not closed. The connection's current state is open. - SQL Server & C#

I'm currently working on the login form of a school management system. The thing is that when I try to log in, I get an error:
System.InvalidOperationException: The connection was not closed. The connection's current state is open
It says that the error is on the 30th line of code but I can't seem to find a way to solve it.
Here's the code of the method in which the error occurs:
public void LoginTeacher()
{
try
{
command = new SqlCommand("TeacherLogin", connection);
command.CommandType = CommandType.StoredProcedure;
connection.Open(); // This is the 30th line.
command.Parameters.AddWithValue("#username", Txt_User.Text);
command.Parameters.AddWithValue("#password", Txt_Pass.Text);
SqlDataReader dataReader = command.ExecuteReader();
if (dataReader.Read())
{
TeacherDash teacherDash = new TeacherDash();
this.Hide();
teacherDash.lblusertype.Text = dataReader[1] + " " + dataReader[2].ToString();
teacherDash.ShowDialog();
this.Close();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
connection.Close();
}
}
Immediately after that error is shown there is another one that says:
System.InvalidOperationException: Invalid attempt to call CheckDataIsReady when reader is closed
and points to line 71 which is the following:
public void Login()
{
try
{
command = new SqlCommand("SP_USER_LOGIN", connection);
command.CommandType = CommandType.StoredProcedure;
connection.Open();
command.Parameters.AddWithValue("#user", Txt_User.Text);
command.Parameters.AddWithValue("#pass", Txt_Pass.Text);
SqlDataReader dataReader = command.ExecuteReader();
if (dataReader.Read())
{
LoginTeacher();
if (dataReader[10].Equals("Admin"))
{
AdminDash adminDash = new AdminDash();
this.Hide();
adminDash.lblusertype.Text = dataReader[1] + " " + dataReader[2].ToString();
adminDash.ShowDialog();
this.Close();
}
There's more code after that but I don't find it relevant since it's the same thing but with the different type of users.
Thanks in advance!
You could try changing your TeacherLogin() method to something like the following:
public void TeacherLogin()
{
try
{
using(SqlConnection con = new SqlConnection("connection string"))
{
using(SqlCommand cmd = new SqlCommand("TeacherLogin"))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#username", Txt_User.Text);
cmd.Parameters.AddWithValue("#password", Txt_Pass.Text);
cmd.Connection = con;
con.Open();
using(SqlDataReader dr = cmd.ExecuteReader())
{
while(dr.Read())
{
TeacherDash teacherDash = new TeacherDash();
this.Hide();
teacherDash.lblusertype.Text = string.Format("{0} {1}", dr[1], dr[2]);
teacherDash.ShowDialog();
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
There's no need to use finally{} to close the connection as its all wrapped in a using() block, it will close and dispose on its own when the code leaves the block. I'd always recommend using SQL connections and commands in this way as not doing so can cause issues by leaving connections open.
Database object need to be closed and disposed. Keeping them local to the method where they are used lets you make sure this happens. using blocks take care of this for you.
I used a DataTable instead of testing with the reader because the connection must remain open as long as the reader is in use. Opening and closing the connection in the briefest possible time is important.
Please don't use .AddWithValue. See http://www.dbdelta.com/addwithvalue-is-evil/
and
https://blogs.msmvps.com/jcoehoorn/blog/2014/05/12/can-we-stop-using-addwithvalue-already/
and another one:
https://dba.stackexchange.com/questions/195937/addwithvalue-performance-and-plan-cache-implications
Here is another
https://andrevdm.blogspot.com/2010/12/parameterised-queriesdont-use.html
Of course you will have to check your database for the real datatypes and field size to have the correct .Add method.
public void LoginTeacher()
{
DataTable dt = new DataTable();
using (SqlConnection cn = new SqlConnection("your connection string"))
using (SqlCommand cmd = new SqlCommand("TeacherLogin", cn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#username",SqlDbType.VarChar,100 ).Value = Txt_User.Text;
cmd.Parameters.Add("#password",SqlDbType.VarChar, 100 ).Value =Txt_Pass.Text;
cn.Open();
dt.Load(cmd.ExecuteReader());
} //Your connection and command are both disposed
if (dt.Rows.Count > 0)
{
TeacherDash teacherDash = new TeacherDash();
teacherDash.lblusertype.Text = $"{dt.Rows[0][1]} {dt.Rows[0][2]}";
teacherDash.ShowDialog();
Close();
}
else
MessageBox.Show("Sorry, login failed");
}

Complicated Report Requires SQL Temp File C# Returns No Rows

Here is the problem. I have to write a long, hella complicated report. I don't think I can do it using only C# so I thought the best thing was a temporary SQL table. So I wrote this code and I always get no rows and I know there is data in the table.
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand();
SqlDataReader reader;
cmd.CommandText = "SELECT * INTO ##temp FROM Customers";
cmd.Connection = connection;
connection.Open();
reader = cmd.ExecuteReader();
if (reader.HasRows)
{ MessageBox.Show("ROWS"); }
else
{ MessageBox.Show("NO ROWS"); }
connection.Close();
So I thought, maybe I need another reader to SELECT * FROM ##TEMP but it always crashes telling me that my reader is already open (I used reader = cmd.ExecuteReader();). Please help.
I found the answer:
cmd.Connection = connection;
connection.Open();
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT * from ##temp";
reader = cmd.ExecuteReader();
First run ExecuteNonQuery, then change the command then run ExecuteReader
Your question fails to justify the use of a temporary table. There really is no reason for you not to simply read the data directly from the actual table (unless your question omitted important and relevant details).
Also, it's a good idea to get in the habit of disposing the db objects with using blocks to clean up properly even in the event of exceptions.
Here is what the code could look like:
using (var connection = new SqlConnection(connectionString))
{
using (var cmd = new SqlCommand("SELECT * FROM Customers", connection))
{
connection.Open();
using (var reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
MessageBox.Show("ROWS");
}
else
{
MessageBox.Show("NO ROWS");
}
}
}
}

Having issue in connection string of already an open DataReader

There is already an open DataReader associated with this Command which
must be closed first.
I m facing this issue when same person open the same page at same time on different system.
I have searched a lot on this but found no successful solution.
I have tired :
MultipleActiveResultSets = true in connection string
Increasing Connection waiting time
Verified all connection are closed
This issue comes only when above condition created. Kindly let me know solution which really works
this my connection function which i m using
public DataSet SelectDs(string str)
{
DataSet ds = new DataSet();
if (con.State == ConnectionState.Closed)
{
con.ConnectionString = ConStr;
con.Open();
}
cmd.CommandText = str;
cmd.Connection = con;
cmd.CommandTimeout = 12000;
adpt.SelectCommand = cmd;
adpt.Fill(ds);
con.Close();
return ds;
}
It is a mortal sin to use a global connection object in that way. It is bad (very bad) in WinForms applications, but in ASP.NET is deadly. (as you have discovered)
The usage pattern for a disposable object (and an expensive one like the connection) is
CREATE, OPEN, USE, CLOSE, DESTROY
The Connection Pooling mechanism exist to make easier the usage of this pattern.
Instead you try to work against it and you pay the consequences.
Your code should be rewritten as
public DataSet SelectDs(string str)
{
DataSet ds = new DataSet();
using(SqlConnection con = new SqlConnection(constring)) // CREATE
using(SqlCommand cmd = new SqlCommand(str, con)) // CREATE
{
con.Open(); // OPEN
cmd.CommandTimeout = 12000;
using(SqlAdapter adpt = new SqlAdapter(cmd)) // USE
adpt.Fill(ds);
return ds;
} // CLOSE & DESTROY
}
How about putting inside a Using statement like
using(SqlConnection connection = new SqlConnection("connection string"))
{
connection.Open();
using(SqlCommand cmd = new SqlCommand("SELECT * FROM SomeTable", connection))
{
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader != null)
{
while (reader.Read())
{
//do something
}
}
} // reader closed and disposed up here
} // command disposed here
} //connection closed and disposed here
in finally clause use this
if (readerObj.IsClosed == false)
{
readerObj.Close();
}
I think you should also dispose your command object before returning dataset.
try cmd.Dispose() after con.close()

Sending several SQL commands in a single transaction

I have a huge list of INSERT INTO ... strings. Currently I run them with:
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
foreach (var commandString in sqlCommandList)
{
SqlCommand command = new SqlCommand(commandString, connection);
command.ExecuteNonQuery();
}
}
I see that each ExecuteNonQuery() also executes commit.
Is there a way to insert all rows in a single transaction (commit in the end)?
The reason I want a single transaction is to make my "inserts" process faster. Will a single transaction also make it quicker?
Its recommended to use SQL transaction in case you are executing Multiple queries in one thread , you can have it like this :
SqlTransaction trans;
try
{
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
trans = connection.BeginTransaction();
foreach (var commandString in sqlCommandList)
{
SqlCommand command = new SqlCommand(commandString, connection,trans);
command.ExecuteNonQuery();
}
trans.Commit();
}
catch (Exception ex) //error occurred
{
trans.Rollback();
//Handel error
}
You might probably gain some performance by using just one single transaction and command, as follows:
using (SqlConnection connection = new SqlConnection(connectionString))
{
try
{
connection.Open();
using (SqlTransaction trans = connection.BeginTransaction())
{
using (SqlCommand command = new SqlCommand("", connection,trans))
{
command.CommandType = System.Data.CommandType.Text;
foreach (var commandString in sqlCommandList)
{
command.CommandText = commandString;
command.ExecuteNonQuery();
}
}
trans.Commit();
}
}
catch (Exception ex) //error occurred
{
//Handel error
}
}
A little late, but if you are inserting all of the values into the same table, code the SQL insert as "insert into tablex (f1, f2, f3,...) values (#F1,#F2,#F3...)". Create the command and add the parameters #F1..., and then set the Prepare flag on the command. Now as you loop through your list of values to insert, you can set them into the appropriate parameters and then do the ExecuteNonQuery. SQL will pre-parse the command string once, and then use the new parameters each time. This is a bit faster.
Finally, you can execute multiple SQL statements in a single command by appending ';' to each statement, if you must execute the entire string. You can bunch a number of these commands together and make one request to SQL server to execute them.
You can just concatenate the sql and let the server handle it:
using (SqlConnection connection = new SqlConnection(connectionString))
{
string lsSql = string.Empty;
foreach (var commandString in sqlCommandList)
{
lsSql = lsSql + commandString + " ; " + Environment.NewLine;
}
connection.Open();
SqlCommand command = new SqlCommand(lsSql, connection);
command.ExecuteNonQuery();
}
Here is what I use on my daily work, before it a use a foreach for any non-query that I need to run on database. You can see that I'm using the OracleCommand, but if you need you can change to SQL statement
public static void ExecuteDatabaseNonQuery(string command)
{
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
OracleTransaction transaction;
transaction = conn.BeginTransaction(IsolationLevel.ReadCommitted);
cmd.Transaction = transaction;
try
{
cmd.CommandText = command;
var update = cmd.ExecuteNonQuery();
transaction.Commit();
Console.WriteLine("{0} rows updated", update);
}
catch (Exception e)
{
transaction.Rollback();
throw new Exception("Error: " + e);
}
}
Note: If theres any uncommited changes on database this method will wait indefinitely
You can use Parallel for each
using (SqlConnection connection = new SqlConnection(connectionString))
{
List<string> sqlCommandList = new List<string>();
connection.Open();
Parallel.ForEach(sqlCommandList, commandString =>
{
SqlCommand command = new SqlCommand(commandString, connection);
command.ExecuteNonQuery();
});
}

Passing sql statements as strings to mssql with C#?

This is a really, really stupid question but I am so accustomed to using linq / other methods for connecting and querying a database that I never stopped to learn how to do it from the ground up.
Question: How do I establish a manual connection to a database and pass it a string param in C#? (yes, I know.. pure ignorance).
Thanks
using (SqlConnection conn = new SqlConnection(databaseConnectionString))
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = "StoredProcedureName";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#ID", fileID);
conn.Open();
using (SqlDataReader rdr =
cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
if (rdr.Read())
{
// process row from resultset;
}
}
}
}
One uses the SqlCommand class to execute commands (either stored procedures or sql) on SQL Server using ado.net. Tutorials abound.
Here's an example from http://www.csharp-station.com/Tutorials/AdoDotNet/Lesson07.aspx
public void RunStoredProcParams()
{
SqlConnection conn = null;
SqlDataReader rdr = null;
// typically obtained from user
// input, but we take a short cut
string custId = "FURIB";
Console.WriteLine("\nCustomer Order History:\n");
try
{
// create and open a connection object
conn = new
SqlConnection("Server=(local);DataBase=Northwind;Integrated Security=SSPI");
conn.Open();
// 1. create a command object identifying
// the stored procedure
SqlCommand cmd = new SqlCommand(
"CustOrderHist", conn);
// 2. set the command object so it knows
// to execute a stored procedure
cmd.CommandType = CommandType.StoredProcedure;
// 3. add parameter to command, which
// will be passed to the stored procedure
cmd.Parameters.Add(
new SqlParameter("#CustomerID", custId));
// execute the command
rdr = cmd.ExecuteReader();
// iterate through results, printing each to console
while (rdr.Read())
{
Console.WriteLine(
"Product: {0,-35} Total: {1,2}",
rdr["ProductName"],
rdr["Total"]);
}
}
finally
{
if (conn != null)
{
conn.Close();
}
if (rdr != null)
{
rdr.Close();
}
}
}
3 things no one else has shown you yet:
"Stacking" using statements
Setting an explicit parameter type rather than letting .Net try to pick one for you
"var" keyword
.
string sql = "MyProcedureName";
using (var cn = new SqlConnection(databaseConnectionString))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ParameterName", SqlDbType.VarChar, 50)
.Value = "MyParameterValue";
conn.Open();
using (SqlDataReader rdr =
cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
if (rdr.Read())
{
// process row from resultset;
}
}
}

Categories