InvalidOpearationException: There is already an open DataReader - c#

Hello I create controls from SQL via this code:
string query = "SELECT * FROM [schema] WHERE idSch=#id";
SqlCommand com = new SqlCommand(query, con);
com.Parameters.AddWithValue("#id", result);
con.Open();
SqlDataReader read= com.ExecuteReader();
while (read.Read())
{
createLabelCmd((int)read["x"], (int)read["y"]);
}
con.Close();
The issue is that createLabelCmd contains SqlCommand and it needs an open SqlConnection
Inside createLabelCmd
String ResultSitting
private void createLabelCmd(int x, int y)
{
for (int i = 0; i < 1; i++)
{
var newLabel = new Label();
newLabel.Location = new Point(x, y);
newLabel.Text = realpocsed.ToString();
string sitting = newLabel.Name;
string sittingSearch = (sitting).Substring(3, 1);
if (sittingSearch != null && kzajezdu == "kZajezdu")
{
string querySitting = "SELECT name, surname FROM klient WHERE sitting = #sitting AND event=#event AND year=#year";
SqlCommand cmdSitting = new SqlCommand(querySitting, spojeni);
cmdSitting.Parameters.AddWithValue("#sitting", sittingSearch);
cmdSitting.Parameters.AddWithValue("#event", idEvent);
cmdSitting.Parameters.AddWithValue("#year", klientClass.Year());
ResultSitting = cmdSitting.ExecuteScalar().ToString().Trim(); //This might be the issue
}
if (kzajezdu == "kZajezdu")
{
newLabel.MouseHover += delegate(object sender, EventArgs e)
{
ToolTip ToolTip1 = new ToolTip();
ToolTip1.ShowAlways = true;
if (sittingSearch != null)
{
ToolTip1.Show(ResultSitting, newLabel);
}
else { ToolTip1.Show("This sitting is Empty!", newLabel); }
};
}
panel1.Controls.Add(newLabel);
}
I get an Exception: InvalidOpearationException: There is already an open DataReader associated with this Command which must be closed first.
May you please help me solve this out?
Edit as Soner Gönül suggested:
try
{
string query = "SELECT * FROM [schema] WHERE idSch=#id";
SqlCommand com = new SqlCommand(query, con);
com.Parameters.AddWithValue("#id", idSch);
con.Open();
SqlDataReader read= com.ExecuteReader();
while (precti.Read())
{
createLabelCmd((int)read["x"], (int)read["y"]);
}
con.Close();
}

The cause of the problem is outlined in other answers (while a DataReader is open, the connection used by that reader cannot serve other commands), however many fails to talk about MultipleActiveResultSets that has been introduced for this kind of situations
Just change your connection string to include this option and your code will work without any change
Server=yourServer;Database=yourDB;Trusted_Connection=True;MultipleActiveResultSets=true;
To complete the answer, MARS is available starting from SQL Server 2005 and there are minor problems that you should be aware of.

Because when you while loop with your open SqlDataReader, there is an open connection already.
From DataReaders (ADO.NET)
“You can use the ADO.NET DataReader to retrieve a read-only,
forward-only stream of data from a database.
Results are returned as the query executes, and are stored in the
network buffer on the client until you request them using the Read
method of the DataReader”
As a general recomendation, use using like;
using(SqlDataReader read= com.ExecuteReader())
{
while (read.Read())
{
createLabelCmd((int)read["x"], (int)read["y"]);
}
}
Or set this in your connection string;
...MultipleActiveResultSets=true;

I guess you are writing a sitting planner and try to show labels at specific positions. Therefore, you would better select all records from klient table for a given event and put them in a DataSet. Then iterate through it (using a foreach) and create the labels. This way, there is only ONE command that should be sent to database and , obviously, the performance of your application will be much better.
Having said that, I don't understand how your sittingSearch variable work and I think it needs revising.

You can either use a 2nd connection for createLabelCmd or turn on MARS (multiple active results sets) in your initial connection by adding "MultipleActiveResultSets=True" to your connection string.
http://msdn.microsoft.com/en-us/library/h32h3abf.aspx

Setting MARS to True AND
making sure i used ToList(); in my if statements and returns
in the below code i was missing the toList() in both conditions of the if statement, i was getting the error on IIS 8.5 after publishing .. updating the statement to the below worked#!
var varM = (id == 1) ? db.M.Where(x => x.UN== userID).ToList() : db.M.Where(x => x.EUN== userID).ToList();

Related

Using sql tableadapters to update database C#

I am building an application for a group of friends and myself to use for DnD sessions. Part of the program involves taking all of the values that are entered for our characters, items, etc and storing them to a database. I have the database built, and am pulling from the database into the program, however I am unable to return data to the database. I have the data coming into a dataset, and all of my edits are affecting the dataset, but I cannot get anything to affect the actual source database tables.
Below I have the button that I intend to use to update items in the characters' packs. I have both dataadapter, and tableadapter methods included that I have tried.
private void btnaddpack_Click(object sender, EventArgs e)
{
if (txtbxpack.Text != "")
{
/*connection.Open();
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "UPDATE Pack SET Item = (#ItemName)" + "WHERE Id = '" + this.lstpack.SelectedValue + "';";
cmd.ExecuteNonQuery();
cmd.Clone();*/
string packitem = txtbxpack.Text; //will take item from an textbox
this.packTableAdapter.Insert(packitem);
this.Validate();
this.packBindingSource.EndEdit();
this.packTableAdapter.Update(this.dnD_MachineDataSet.Pack);
}
PopulatePack();
Here is my populate code in case someone needs that:
private void PopulatePack()
{
using (connection = new SqlConnection(connectionString)) //this is all about opening the connection to the sqldatabase, normally it would need to be closed, but this uses idisposable, so it will close itself
using (SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM Pack", connection))
{
DataTable packtable = new DataTable();
adapter.Fill(packtable);
lstpack.DataSource = packtable;
lstpack.DisplayMember = "Item";
lstpack.ValueMember = "Id";
}
}
As mentioned above, all of the changes are appearing whenever I re-populate the listboxes that draw upon the dataset, hence why this is an issue of trying to get that data back into the source database. I will make the obligatory "I'm relatively new to using databases" statement as it will do no good to pretend that I am an expert.
Thanks.
In the commented code, you would need to do the following:
assign the connection object to the SqlCommand object's Connection
property
pass the item name to your #ItemName parameter
assign a parameter value to the 'Id' column in the WHERE clause
remove, 'cmd.Clone();', and replace with, 'connection.Close();'
Here is what the code should look like:
connection.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = connection;
cmd.CommandText = "UPDATE Pack SET Item = (#ItemName) WHERE Id = #ID;";
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#ItemName", txtbxpack.Text);
cmd.Parameters.AddWithValue("#ID", this.lstpack.SelectedValue);
cmd.ExecuteNonQuery();
connection.Close();

Update if disabled = 0 in C#

I am making a project in C#- in which one can "vote".
When you run the program you first log in. After you've logged in you have to select a value out of a dropdownlist. After you've selected the teacher you press on a button which votes.
The problem is I don't really know how to let this validate properly. And to check if the person has already voted.
It has to check the column in the database named "disabled" if the value = 1 or 0. If the value = 1 they can't vote and if it's 0 they can.
When the person votes it increases the column aantalStemmen by 1. and the disabled column to 1 aswell. Which gets shown in a datagridview.
And the values in the dropdownlist has to match the 1 in the database.
I have this code:
private void db_connection()
{
try
{
conn = "Data Source=localhost;Initial Catalog=docent;Integrated Security=True";
connect = new SqlConnection(conn);
connect.Open();
}
catch (SqlException e)
{
throw;
}
}
private bool validate_disabled(string favoriet)
{
db_connection();
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "Select disabled from leerling";
cmd.Connection = connect;
SqlDataReader disabled = cmd.ExecuteReader();
if (disabled.Read())
{
connect.Close();
return true;
}
else
{
connect.Close();
return false;
}
}
private void btnStem_Click(object sender, EventArgs e)
{
string favoriet = cmbFavoriete.Text;
db_connection();
SqlCommand cmd = new SqlCommand();
bool r = validate_disabled(favoriet);
if(r){
cmd.CommandText = "UPDATE docent SET aantalStemmen = aantalStemmen + 1 where docentid=#id";
cmd.Parameters.AddWithValue("#id", cmbFavoriete.Text);
}
else
{
MessageBox.Show("You have already voted.");
}
}
my tables in my database looks like this:
Thanks in advance, I've been struggling really hard with this as I'm still a rookie in C#.
I will try an answer to cover more aspects of your code (many already mentioned in comments):
1) Declare your connection string outside of your methods. Also choose meaningful variable names - you will than yourself in a few months when you revisit the code.
private const String ConnectionStr = "Data Source=localhost;Initial Catalog=docent;Integrated Security=True";
2. Appropriate names for methods - also, try to use Camel or Pascal case for method names.
3. Pay attention to possible exceptions. SQLException is not the only possible exception when constructing or opening an SqlConnection, so it is better to catch anything that might occur
private SqlConnection createConnection
{
try
{
connect = new SqlConnection(ConnectionStr);
connect.Open();
}
// this is laziness, but it is better than before
catch (Exception e)
{
// best to log the real error somewhere
throw;
}
}
4. Dispose connection and other disposables like SqlCommand. Also var might save some typing (just hover your mouse over the keyword and you will see the actual type).
SqlConnection allows to directly create a command to be executed using that particular connection by using CreateCommand.
Since your are expecting a single value (scalar) (or a single row with a single column), you can use ExecuteScalar method. So, no more reader.
private bool isDisabled(string favoriet)
{
using (var connection = createConnection())
{
using (var cmd = new connection.CreateCommand())
{
cmd.CommandText = "Select disabled from leerling where leerlingnummer = #number";
cmd.Parameters.AddWithValue("#number", favoriet);
// for simplicity I have assumed that it will always find a value. This should be checked
var disabled = Convert.ToBoolean(cmd.ExecuteScalar());
return disabled;
}
}
}
5. Try not to mix UI logic with database logic (they are usually put in different assemblies)
private void castVote(String favoriete)
{
using (var connection = createConnection())
{
using (var cmd = new connection.CreateCommand())
{
cmd.CommandText = "UPDATE docent SET aantalStemmen = aantalStemmen + 1 where docentid = #id";
cmd.Parameters.AddWithValue("#id", cmbFavoriete.Text);
// command must be actually executed, otherwise nothing happens
cmd.ExecuteNonQuery();
}
}
}
private void btnStem_Click(object sender, EventArgs e)
{
string favoriet = cmbFavoriete.Text;
bool r = isDisabled(favoriet);
if (r)
castVote(favoriet);
// maybe, it would make sense to also notify the user that the vote has been cast
else
MessageBox.Show("You have already voted.");
}
6. Use EntityFramework - in order to avoid the troubles related to handling commands and readers, you can use an ORM to do the dirty work for you.
I would suggest you use bit Data Type (0 - false, 1 - true) instead of int Data Type in your table. It does exactly what you need and you don't have to use int for this.
This means you could change your validate_disabled method to use something like this:
cmd.CommandText = "SELECT disabled FROM leerling WHERE disabled = 1 AND leerlingnummer = #favoriet";
cmd.Parameters.AddWithValue("#favoriet", favoriet);
I've assumed string favoriet is equal to leerlingnummer in your table. After you've executed that query, you would simply check if the query contains more than 0 records - if more than 0 records that means the person does not have permission to vote.

C# MySql funktion (command string in, return reader)

I've written a function to perform MySQL statements. In this function I give in a statement and get back the MySqlDataReader, but the problem is my function do not close the connection. After a short while of using the Programm, it crashs because the new connection can't be open. This is the error i got by trying open the new connection:
error connecting: 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.
My code look like this:
MySQL Class:
class mySql
{
string cs = "server=123.123.123.123;" +
"uid=abcabc;" +
"pwd=123456;" +
"database=overflow_test;";
private MySqlConnection conn_f() // create a Connection
{
MySql.Data.MySqlClient.MySqlConnection conn;
conn = new MySql.Data.MySqlClient.MySqlConnection();
conn.ConnectionString = cs;
try
{
conn.Open();
return conn;
}
catch
{
return null;
}
}
public MySqlDataReader CMD_f(string comand) //execute SQL Command
{
MySql.Data.MySqlClient.MySqlCommand cmd;
cmd = new MySql.Data.MySqlClient.MySqlCommand();
MySqlConnection conn = conn_f();
cmd.Connection = conn;
cmd.CommandText = comand;
cmd.Prepare();
rdr = cmd.ExecuteReader();
return rdr;
}
}
and an example how i use it Main Class
class main{
mySql DB = new mySql();
public void main(){
MySqlDataReader rdr = DB.CMD_f("SELECT * FROM tbl_kategorie");
int i = 0;
while (rdr.Read())
{
string str = rdr.GetString(1);
Console.WriteLine(str);
}
}
Has someone an Idea to solve the Problem.
Sincere regards LFS96 (Fabian Harmsen)
The main problem is the inbalance in the code. The CMD_f method creates a connection, but doesn't take responsibility for it. You should rather make the method that creates the connection public, so that you can take responsibility for it in the code that can close it.
That's a bigger change, so first let's look at a smaller change to fix the code.
The data reader should expose the data connection as the Connection property. I don't see that in the documentation, so it's possible that it doesn't, but the data readers in the .NET framework does.
If the property is exposed, then you can make a quick fix to make the code work with minimal change:
MySqlDataReader rdr = DB.CMD_f("SELECT * FROM tbl_kategorie");
int i = 0;
while (rdr.Read())
{
string str = rdr.GetString(1);
Console.WriteLine(str);
}
rdr.Close();
rdr.Connection.Close();
If you make the conn_f method public and don't call it in the CMD_f method, you can write much more robust code, that never leaves a connection or data reader hanging even if there is an error. That's a bigger change in the code, but definitely worth the effort when you have time to implement and test it. Using a using block to make sure that the objects are always disposed correctly makes the code much more resilient:
using (MySqlConnection conn = DB.conn_f()) {
using (MySqlDataReader rdr = DB.CMD_f(conn, "SELECT * FROM tbl_kategorie")) {
int i = 0;
while (rdr.Read()) {
string str = rdr.GetString(1);
Console.WriteLine(str);
}
}
}
Side note: Identifiers in C# tend to be descriptive. I suggest names like DB.CreateConnection and DB.ExecuteReader rather than DB.conn_f and DB.CMD_f.

How do I properly use a SqlDataReader?

I have 2 methods as below :
internal static SqlDataReader SelectData(string sql)
{
using (var sqlConnection = new SqlConnection(Constant.ConnectionString))
{
sqlConnection.Open();
var sqlCommand = new SqlCommand(sql, sqlConnection);
var dataReader = sqlCommand.ExecuteReader();
return dataReader;
}
}
============
And using this method as :
var dataReader = SelectData(---some sql ---);
private void AddData(dataReader)
{
while (dataReader.Read())
{
Employee e = new Employee();
e.FirstNamei = dataReader["Name"].ToString();
}
dataReader.Close();
}
I know we can merge this two method, but I am looking at better way write this, OR this can cause some problem??
Actually you are in fact leaving yourself open a bit. You really want to write it like this:
using (SqlConnection cnn = new SqlConnection(cnnString))
using (SqlCommand cmd = new SqlCommand(sql, cnn))
{
// use parameters in your SQL statement too, so you can do this
// and protect yourself from SQL injection, so for example
// SELECT * FROM table WHERE field1 = #parm1
cmd.Parameters.AddWithValue("#parm1", val1);
cnn.Open();
using (SqlDataReader r = cmd.ExecuteReader())
{
}
}
because you need to make sure these objects get disposed. Further, by going this direction you don't need dataReader.Close(). It will get called when it gets automatically disposed by the using statement.
Now, wrap that collection of statements inside a try...catch and you're in business.
A couple of things
1) Since you're closing your connection on SelectData, dataReader should blow up on AddData as it requires an open connection
2) AddData shouldn't close dataReader as he didn't open it.
3) Maybe you're hiding some code but I don't see that you use Employee instance created on AddData
Technically, first method would be correct if you would do
sqlCommand.ExecuteReader(CommandBehavior.CloseConnection);
Then, your client would close the reader and your connection will be closed as well.
Second example would also be correct if you didn't close the reader inside of it. There is no criminal in passing the reader to a method just to iterate it. but it has to be controlled from where it was created. How you open and dispose of it - this is different question.

It don't get any data from DataReader

i work with N tiers tec in C# for ado, trying to make it easy to use and capable to change any database kind with out write all the cod all over again ,
my code here doesn't get any error but it doesn't get any values to my textbox
(i am trying to get data from table to many textboxs to update it later)
and here how code works:{
at first i make some functions to take any set any kind of parameters or set any command and then i make other function to to execute what ever i set or get from database all that Function i build it in folder name (Data Access Layer)
then i made other folder (Data Build layer)to take use all those function for what ever i want to do in any page (insert , update , delete , Select),
the last think i do it to call the function i made at at (Data Build layer) to my page or control ,
i do all that because if i Change the database Type ,i change only one class and other classes still the same
i hope i explain enough (sorry for my English not good enough)}
Code :
Class DataAccessLayer
public static void Setcommand (SqlCommand cmd,CommandType type,string commandtext)
{
cmd.CommandType=type;
cmd.CommandText=commandtext;
}
public static void AddSQLparameter(SqlCommand cmd, int size,SqlDbType type,object value,string paramName,ParameterDirection direction)
{
if (cmd == null)
{
throw (new ArgumentException("cmd"));
}
if (paramName == null)
{
throw (new ArgumentException("paramName"));
}
SqlParameter param=new SqlParameter();
param.ParameterName= paramName;
param.SqlDbType=type;
param.Size=size;
param.Value=value;
param.Direction=direction;
cmd.Parameters.Add(param);
}
public static SqlDataReader ExecuteSelectCommand(SqlCommand cmd)
{
if (cmd == null)
{
throw (new ArgumentNullException("cmd"));
}
SqlConnection con = new SqlConnection();
cmd.Connection = con;
con.Open();
SqlDataReader dr = cmd.ExecuteReader();
con.Close();
return dr ;
}
Class DatabuildLayer
SqlCommand com;
public DatabuildLayer()
{
com = new SqlCommand();
//
// TODO: Add constructor logic here
//
}
public SqlDataReader SelectCatalog(int catid)
{
DataAccessLayer.Setcommand(com, CommandType.Text, "select catname,catdescription,photo from category where catid=#catid" );
DataAccessLayer.addSQLparameter(com,16,SqlDbType.Int,catid,"#catid",ParameterDirection.Input);
return DataAccessLayer.ExecuteSelectCommand(com);;
}
and here my last code that retrieve my data to some textbox
in my Pageload :
protected void Page_Load(object sender, EventArgs e)
{
DatabuildLayer= new DatabuildLayer();
SqlDataReader dr ;
dr = obj.SelectCatalog(catselectddl.SelectedIndex);
if (dr.Read())
{
catnametxt.Text = dr["catname"].ToString();
catdestxt.Text = dr["catdescription"].ToString();
}
}
Is it possible that the query is returning nothing, and dr.Read() is returning false? Assuming the code actually executes (it is hard to tell from here) that is probably the only thing that would stop it working - either that or empty columns.
For what it is worth I think that your code needs to be tidied up a bit from a structural and conventions point of view. You should probably look through your code and consider the naming guidelines for the .NET framework. When others read your code they will want it formatted and consistent with this documentation. http://msdn.microsoft.com/en-us/library/xzf533w0(v=vs.71).aspx
Further, most people doing ASP.NET these days try to look for some way to inject external dependencies (such as databases) into their code using a framework like WebFormsMVP available at
http://webformsmvp.com/ in conjunction with an IoC container like autofac available at http://code.google.com/p/autofac/.
Using this approach you can push all external dependencies out of your application behind interfaces which would make it fairly trivial to plug in a different database engine.
Your current wrapper code is not doing anything particularly useful (just subsituting the existing methods or your own tht do the same thing), and it is not closing the connections correctly. It is... a bit of a mess.
If you aren't already massively familiar with the raw ADO.NET interfaces, then maybe consider something like "dapper" which will do all this for you, with a sane API:
short catid = 16;
using(var conn = GetOpenConnection()) {
var row = conn.Query(
"select catname,catdescription,photo from category where catid=#catid",
new { catid }).FirstOrDefault();
if(row != null) {
string name = row.catname, desc = row.catdescription;
// ...
}
}
Or if you have a class with CatName / CatDescription properties:
var obj = conn.Query<Catalogue>(
"select catname,catdescription,photo from category where catid=#catid",
new { catid }).FirstOrDefault();
from my experience, when you close a connection associated with a DataReader, nothing can be retrieved from the reader anymore.
//You closed the connection before returning the dr in the your method below:
public static SqlDataReader ExecuteSelectCommand(SqlCommand cmd)
{
if (cmd == null)
{
throw (new ArgumentNullException("cmd"));
}
SqlConnection con = new SqlConnection();
cmd.Connection = con;
con.Open();
SqlDataReader dr = cmd.ExecuteReader();
con.Close(); //here your connection was already closed
return dr ; //this dr is disconnected
}

Categories