OracleDataReader. Error: Invalid operation. The connection is closed - c#

I encountered error when get something from Oracle DB.
Here is my code:
public virtual IDataReader LoadDataReaderWithSqlString(string strQuery, ISessionScope session)
{
var s = GetSession(session);
using (var connection = s.Connection)
{
var command = connection.CreateCommand();
command.Connection = connection;
if (connection.State == ConnectionState.Closed || connection.State == ConnectionState.Broken)
connection.Open();
command.CommandType = CommandType.Text;
command.CommandText = s.CreateSQLQuery(strQuery).ToString();
s.Transaction.Enlist(command); // Set the command to exeute using the NHibernate's transaction
using (var dataReader = command.ExecuteReader())
{
if(dataReader.Read())
return dataReader;
}
}
return null;
}
When I debugging, I was able to see the return value in dataReader.
I using NHibernate to Run Raw SQL. I want to return DataReader. Anyone can help me please?

Are you are trying to return opened IDataReader? The problem is that you wrap your ExecuteReader in using statement. using means that your dataReader will be disposed after code inside of using is executed. So you return disposed object. The solution is: remove using:
var dataReader = command.ExecuteReader();
if(dataReader.Read())
return dataReader;
And same for connection object.
ADD
As David mentioned in comments you might want to avoid resources leak (I mean the case when connection was opened, but command was not executed), then you should handle exceptions like this:
public virtual IDataReader LoadDataReaderWithSqlString(string strQuery, ISessionScope session)
{
try
{
var s = GetSession(session);
var connection = s.Connection;
var command = connection.CreateCommand();
command.Connection = connection;
if (connection.State == ConnectionState.Closed || connection.State == ConnectionState.Broken)
connection.Open();
command.CommandType = CommandType.Text;
command.CommandText = s.CreateSQLQuery(strQuery).ToString();
s.Transaction.Enlist(command); // Set the command to exeute using the NHibernate's transaction
try
{
var dataReader = command.ExecuteReader();
if(dataReader.Read())
return dataReader;
}
catch (DbException)
{
// error executing command
connection.Close();
return null; // or throw; // it depends on your logic
}
}
catch (DbException)
{
// if connection was not opened
return null; // or throw; // it depends on your logic
}
return null;
}

that is because you having
using (var connection = s.Connection) and using (var dataReader = command.ExecuteReader())
using block will dispose the object ( here connection and dataReader)
remove the using block if you need to return the dataReader

Related

How to access the first column in SQL and why does this code give me error?

Can someone tell my why my expectedNumber reader throws an error
The name reader does not exist in its current context
As far as I can see all this is doing is reading the first row and first column, don't understand why the reader is throwing a tantrum.
It doesn't like the line:
ExpectedNumber = reader.GetInt16(0);
The query is :
SELECT TOP (1) [ExpectedNumber]
FROM [dbo].[MyDatabase]
WHERE id = '{0}'
Code:
try
{
using (SqlCommand cmd = new SqlCommand(string.Format(Query, id), Connection))
{
Connection.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
// Check is the reader has any rows at all before starting to read.
if (reader.HasRows)
{
int ExpectedNumber = 0;
// Read advances to the next row.
while (reader.Read() == true)
{
// To avoid unexpected bugs access columns by name.
ExpectedNumber = reader.GetInt16(0);
}
Connection.Close();
return ExpectedResult;
}
Assert.Fail("No results returned from expected result query");
return 0;
}
}
}
catch (Exception e)
{
Connection.Close();
throw;
}
You should escape your query parameters, otherwise your code is vulnerable to SQL injection attacks, also, by using command parameters as in the example below you can make sure you are using the right data type (it seems you are trying to pass an int id as a string).
You are just trying to get one value so you don't need to use a reader and can use ExecuteScalar instead.
Finally, you don't need to handle closing the connection if you enclose it in a using block so you can avoid the try catch block as well.
string query = "SELECT TOP (1) [ExpectedNumber] FROM [dbo].[MyDatabase] WHERE id = #id";
using (var connection = new SqlConnection("connStr"))
{
connection.Open();
using (var cmd = new SqlCommand(query, connection))
{
cmd.Parameters.Add("#id", SqlDbType.Int).Value = id;
object result = cmd.ExecuteScalar();
if (result != null && result.GetType() != typeof(DBNull))
{
return (int)result;
}
Assert.Fail("No Results Returned from Expected Result Query");
return 0;
}
}
Note: this code assumes you are using SQL Server, for other systems the format of the parameters in the connection string might change, e.g. for Oracle it should be :id instead of #id.

How to execute another MySqlDataReader for each read ID?

I'm trying to get from my database some data, each of that data may have some attributes, so my logic was while i'm getting all data so while the MySqlDataReader is executed i was going to execute the query for each id of data i got to get it's attributes.
But i run in to error: 'There is already an open DataReader associated with this Connection' so my guess is that i can't run at the same time the MySqlDataReader so at this point, which would be the best approach to get attributes for each data?
Should i just cycle on each Plu element after i've added them to the list or is there a better solution?
Here is the function where i get the data (Plu object)
public IEnumerable<Plu> GetPlu(string piva, int menu)
{
string connectionString = $"CONSTR";
using var connection = new MySqlConnection(connectionString);
connection.Open();
var sql = #"QUERY";
using var cmd = new MySqlCommand(sql, connection);
cmd.Parameters.AddWithValue("#menu", menu);
cmd.Prepare();
using MySqlDataReader reader = cmd.ExecuteReader();
List<Plu> plu = new List<Plu>();
while (reader.Read())
{
plu.Add(new Plu(
(int)reader["ID_PLUREP"],
(string)reader["CODICE_PRP"],
(string)reader["ESTESA_DES"],
(string)reader["DESCR_DES"], (float)reader["PRE_PRP"],
reader.IsDBNull(reader.GetOrdinal("IMG_IMG")) ? null : (string)reader["IMG_IMG"],
Attributi(connection, (int)reader["ID_PLUREP"])
));
}
return plu;
}
And here is function Attributi which return the IEnumerable of attributes for each Plu
public IEnumerable<Plu.Attributi> Attributi(MySqlConnection connection, int idplu)
{
var sql = #"QUERY";
using var cmd = new MySqlCommand(sql, connection);
cmd.Parameters.AddWithValue("#idplu", idplu);
cmd.Prepare();
List<Plu.Attributi> attributi = new List<Plu.Attributi>();
using MySqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
attributi.Add(new Plu.Attributi(
reader.IsDBNull(reader.GetOrdinal("BCKCOL_ATT")) ? null : (string)reader["BCKCOL_ATT"],
reader.IsDBNull(reader.GetOrdinal("FORCOL_ATT")) ? null : (string)reader["FORCOL_ATT"],
reader.IsDBNull(reader.GetOrdinal("DESCR_ATT")) ? null : (string)reader["DESCR_ATT"]
));
}
return null;
}
You can't use an open connection with a reader already executing. Open a new connection in Attributi.
public IEnumerable<Plu.Attributi> Attributi(int idplu)
{
var sql = #"QUERY";
using var connection = new MySqlConnection(connectionString)
{
connection.Open();
using var cmd = new MySqlCommand(sql, connection)
{
cmd.Parameters.AddWithValue("#idplu", idplu);
cmd.Prepare();
List<Plu.Attributi> attributi = new List<Plu.Attributi>();
using MySqlDataReader reader = cmd.ExecuteReader()
{
while (reader.Read())
{
attributi.Add(new Plu.Attributi(
reader.IsDBNull(reader.GetOrdinal("BCKCOL_ATT")) ? null : (string)reader["BCKCOL_ATT"],
reader.IsDBNull(reader.GetOrdinal("FORCOL_ATT")) ? null : (string)reader["FORCOL_ATT"],
reader.IsDBNull(reader.GetOrdinal("DESCR_ATT")) ? null : (string)reader["DESCR_ATT"]
));
}
return null;
}
}
}
BTW, your usage of using is totally off. You need a block after the using statement where you deal with everything regarding the IDisposable object.
EDIT: Apparently that's a new .NET Core 3.1 feature.
For the more general case, my experience with MySQL has lead me to always "free" my reader with:
MySqlDataReader reader = cmd.ExecuteReader();
DataTable dataTable = new DataTable();
dataTable.Load(reader);
Then working from the DataTable rather than the MySqlDataReader, you can then reuse the connection as you prefer.

What are the best practices working with Oracle.DataAccess.Client?

I'm going over a lengthy data access code of a somewhat older app. Every function is calling a stored procedure to select something from Oracle DB. Every function more or less looks like the code below:
public List<SomeObject> GetMeSomethingFromDB(string myParam, int anotherParam)
{
OracleConnection conn = null;
OracleDataReader dataReader = null;
try
{
conn = new OracleConnection(Settings.ConnectionString);
conn.Open();
var cmd = new OracleCommand("STORED_PROC_NAME", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new OracleParameter("IN_MY_PARAM", OracleDbType.Varchar2)).Value = myParam;
cmd.Parameters.Add(new OracleParameter("IN_ANOTHER_PARAM", OracleDbType.Int32)).Value = anotherParam;
cmd.Parameters.Add(new OracleParameter("OUT_REF_CURSOR", OracleDbType.RefCursor)).Direction = ParameterDirection.Output;
dataReader = cmd.ExecuteReader();
List<SomeObject> result = new List<SomeObject>();
if (dataReader == null || !dataReader.HasRows) return result;
while (dataReader.Read())
{
SomeObject someObject = new SomeObject
{
SomeId = (int)dataReader["SOME_ID"],
SomeStringValue = dataReader["SOME_STRING_VALUE"].ToString()
};
result.Add(someObject);
}
return result;
}
catch (Exception e)
{
throw e;
}
finally
{
if (dataReader != null)
{
dataReader.Close();
dataReader.Dispose();
}
if (conn != null)
{
if (conn.State == ConnectionState.Open) conn.Close();
conn.Dispose();
}
}
}
My questions are:
Some functions use class level OracleConnection variable instead. What is preferred - function level or class level variable?
Is the check dataReader == null necessary? Would it ever be NULL after cmd.ExecuteReader() call?
Functions differ when it comes to connection Close/Dispose and reader Close/Dispose. What is the correct way/order in which to close/dispose? Wouldn't the reader automatically Close/Dispose if the connection is disposed?
I'm looking to hook up Oracle.ManagedDataAccess.Client to this project in the near future. Will anything in this code change to work with managed data access client?
Anything else, any best practices/suggestions are welcomed.
Thank you.
The using statement will simplify a lot your code.
public List<SomeObject> GetMeSomethingFromDB(string myParam, int anotherParam)
{
using (OracleConnection conn = new OracleConnection(Settings.ConnectionString))
using (OracleCommand cmd = new OracleCommand("STORED_PROC_NAME", conn))
{
conn.Open();
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new OracleParameter("IN_MY_PARAM", OracleDbType.Varchar2)).Value = myParam;
cmd.Parameters.Add(new OracleParameter("IN_ANOTHER_PARAM", OracleDbType.Int32)).Value = anotherParam;
cmd.Parameters.Add(new OracleParameter("OUT_REF_CURSOR", OracleDbType.RefCursor)).Direction = ParameterDirection.Output;
using (OracleDataReader dataReader = cmd.ExecuteReader())
{
while (dataReader.Read())
{
SomeObject someObject = new SomeObject
{
SomeId = (int)dataReader["SOME_ID"],
SomeStringValue = dataReader["SOME_STRING_VALUE"].ToString()
};
result.Add(someObject);
}
}
}
return result;
}
Use always a local connection object and include it in a using
statement to have a correct closing and disposal of the object (the
same holds true also for the OracleDataReader and OracleCommand). This will free your server from the memory and threads required to keep the connection with your code and performances are guaranteed by the connection pooling enabled by ADO.NET providers
No, the call is not necessary and neither the call to HasRows if you
plan to loop over the result. The reader returns false if there are
no rows or if you reach the end of the data set
See the point about the using statement. Proper using statements will remove this problem from your burdens.
You should't have any problem with this code if you use the ODP
provider from Oracle
There is no need to have a try catch if you only want to rethrow the
exception. Just let it bubble up to the upper level without
disrupting the stack trace with a throw e and all the code required in the finally statement is implicitly added by the compiler in the using closing curly brace.

Connecting to a database based on a course I follow

I’m following an online course and in the course they explain how you can retrieve data from a database. Creating the connection and commands are done by a DbProviderFactories class. I understand the code in the course but is using using for the connection, command and reader necessary? Also, are the null checks necessary? The code looks cluttered and if you have a lot of models in your database (Continent, Country, Currency, …) it would require a lot of copy/paste which is bad?
So the question really is, is the code below rather good or bad and what could be improved upon? The goal is to use SQLite as database provider. Does this work with the approach below?
public static ObservableCollection<Continent> GetContinents()
{
var continents = new ObservableCollection<Continent>();
var provider = ConfigurationManager.ConnectionStrings["DbConnection"].ProviderName;
var connectionString = ConfigurationManager.ConnectionStrings["DbConnection"].ConnectionString;
using (var connection = DbProviderFactories.GetFactory(provider).CreateConnection())
{
if (connection == null) return null;
connection.ConnectionString = connectionString;
connection.Open();
using (var command = DbProviderFactories.GetFactory(provider).CreateCommand())
{
if (command == null) return null;
command.CommandType = CommandType.Text;
command.Connection = connection;
command.CommandText = "SELECT * FROM Continent";
using (var reader = command.ExecuteReader())
while (reader.Read())
continents.Add(new Continent(reader["Code"].ToString(), reader["EnglishName"].ToString()));
}
}
return continents;
}
using using for the connection, command and reader necessary?
Yes.
Here I commented the code
using (var command = DbProviderFactories.GetFactory(provider).CreateCommand()) // here you've created the command
{
if (command == null) return null;
command.CommandType = CommandType.Text;
command.Connection = connection;
command.CommandText = "SELECT * FROM Continent";
using (var reader = command.ExecuteReader()) //Here you're reading what the command returned.
while (reader.Read())
continents.Add(new Continent(reader["Code"].ToString(), reader["EnglishName"].ToString()));
}
Also, are the null checks necessary?
It could return null data so yes absolutely
The code looks cluttered
Such is the coder life brotha. Using loops for objects will save on space.

How to return a MySqlDataReader before the reader is closed [duplicate]

This question already has answers here:
Return DataReader from DataLayer in Using statement
(4 answers)
Closed 10 years ago.
Okay, my code is currently:
public MySqlDataReader CreateQuery(string queryString, string connectionString )
{
using (MySqlConnection connection = new MySqlConnection(connectionString))
{
using (MySqlCommand command = new MySqlCommand(queryString, connection))
{
command.Connection.Open();
command.ExecuteNonQuery();
MySqlDataReader reader = command.ExecuteReader();
return reader;
}
}
}
In another function, I've got:
using(MySqlDataReader readers = _connection.CreateQuery(query, connectString))
Currently, in this form, when I return reader into a readers, there is no value. I've stepped through, and verified that at the point of the return, reader had the correct information in it. Yet readers has no values. I'm probably missing something completely silly. But any and all help in this will be appreciated. Thanks!
With the using command, your code is actually going to dispose of the connection and the command before control is returned to the caller. Essentially your connection object and your command object created in the using statements are disposed before the calling code is able to use it so its no longer usable.
You probably want to do something with the results of the query rather than the reader itself. Perhaps load it into a table? Something like:
public DataTable CreateQuery(string queryString, string connectionString )
{
DataTable results = new DataTable("Results");
using (MySqlConnection connection = new MySqlConnection(connectionString))
{
using (MySqlCommand command = new MySqlCommand(queryString, connection))
{
command.Connection.Open();
command.ExecuteNonQuery();
using (MySqlDataReader reader = command.ExecuteReader())
results.Load(reader);
}
}
return results;
}
Instead of returning the Datareader or copy all records into an in-memory DataTable that you return as a whole, you could return an IEnumerable<IDataRecord> which is the basic interface a DataReader record implements.
So this should work since the yield causes the Connection to keep alive:
public IEnumerable<IDataRecord> CreateQuery(string queryString, string connectionString)
{
using (MySqlConnection connection = new MySqlConnection(connectionString))
{
using (MySqlCommand command = new MySqlCommand(queryString, connection))
{
command.Connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
yield return (IDataRecord)reader;
}
}
}
}
}
If you want for example take only the first 5 records:
var records = CreateQuery("SELECT * FROM ais.country;", Properties.Settings.Default.MySQL).Take(5);
foreach (IDataRecord rec in records)
{
String country = rec.GetString(rec.GetOrdinal("Name"));
}

Categories