I have a bunch of methods like so in my DAL in VS 2010. When I run the "new" Code Analysis option i get the message - Warning CA2000 object 'comm' is not disposed along all exception paths. Call System.IDisposable.Dispose on object 'comm' before all references to it are out of scope.
I understand I could use another using statement for the SQLCommand however if prefer to do it like I have with the Try/Finally block. My understanding is the Finally block executes last and does the cleanup. Does anyone see anything wrong here with my dispose call?
public List<Product> GetAllProducts()
{
List<Product> prodList = new List<Product>();
using (SqlConnection connection = new SqlConnection(GetConnection()))
{
SqlCommand comm = new SqlCommand("GetAllProducts", connection);
connection.Open();
comm.CommandType = CommandType.StoredProcedure;
SqlDataReader dr = comm.ExecuteReader();
try
{
while (dr.Read())
{
Product obj = new Product();
obj.ProductID = Convert.ToInt32(dr["ProductID"].ToString());
obj.Product = dr["Product"].ToString();
//etc....
prodList.Add(obj);
}
}
finally
{
comm.Dispose();
dr.Close();
}
}
return prodList;
}
}
If any of these three statements throw an exception, comm will not be disposed.
connection.Open();
comm.CommandType = CommandType.StoredProcedure;
SqlDataReader dr = comm.ExecuteReader();
Your try block would need to encompas these statements as well.
Put a using block around the command and dataReader so that they will always be disposed.
public List<Product> GetAllProducts()
{
List<Product> prodList = new List<Product>();
using (SqlConnection connection = new SqlConnection(GetConnection()))
{
using (SqlCommand comm = new SqlCommand("GetAllProducts", connection))
{
connection.Open();
comm.CommandType = CommandType.StoredProcedure;
using (SqlDataReader dr = comm.ExecuteReader())
{
try
{
while (dr.Read())
{
Product obj = new Product();
obj.ProductID = Convert.ToInt32(dr["ProductID"].ToString());
obj.Product = dr["Product"].ToString();
//etc....
prodList.Add(obj);
}
}
}
}
}
return prodList;
}
List<Measure_Type> MeasureList = new List<Measure_Type>();
SqlConnection conn = null;
SqlDataReader rdr = null;
SqlCommand cmd = null;
try
{
conn = new SqlConnection();
conn.ConnectionString = System.Configuration.ConfigurationManager.AppSettings["Conn"];
conn.Open();
cmd = new SqlCommand("SELECT Measure_ID, Mea_Name FROM MeasureTable WHERE IsActive=1", conn);
rdr = cmd.ExecuteReader();
if (rdr.HasRows == true)
{
while (rdr.Read())
{
MeasureTypeList.Add(new Measure_Type { MeasureTypeID = Convert.ToInt32(rdr[0]), MeasureTypeName = rdr[1].ToString() });
}
}
}
catch (Exception ex)
{
ExceptionPolicy.HandleException(ex, "Log");
}
finally
{
cmd.Dispose();
// close the reader
if (rdr != null) { rdr.Close(); }
// Close the connection
if (conn != null) { conn.Dispose(); }
}
return MeasureTypeList;
create conn = new SqlConnection(); inside the try block and then open it to solve this bug.
Related
Why am I getting this error:
Connection state has not been intialized
when I'm using one method in other?
This is my DbConnectio.cs:
public class DbContext
{
public SqlConnection sqlconn = null;
public SqlConnection DbConnection
{
get { return sqlconn; }
set { value = sqlconn; }
}
public DbContext()
{
string cs = ConfigurationManager.ConnectionStrings["CTXDB"].ConnectionString;
sqlconn = new SqlConnection(cs);
}
}
web.config:
<add name="CTXDB"
connectionString="Data Source=Md;Initial Catalog=Md;User ID=sa;Password=123;MultipleActiveResultSets=true"
providerName="System.Data.SqlClient" />
This is my Repo.cs - here I'm implementing my business logic:
DbContext db = new DbContext();
public Employee FindEmpById(int key)
{
SqlConnection conn = db.DbConnection;
try
{
var employee = new Employee();
if (conn.State != System.Data.ConnectionState.Open)
{
conn.Open();
}
SqlCommand cmd = new SqlCommand("Sp_GetEmployeeById", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#EmpId", key);
SqlDataReader rdr = cmd.ExecuteReader();
if (rdr.HasRows == true)
{
while (rdr.Read())
{
employee.Emp_Id = Convert.ToInt32(rdr["Emp_Id"]);
employee.EmpName = rdr["EmpName"].ToString();
employee.Email = rdr["Email"].ToString();
employee.Psw = rdr["Psw"].ToString();
}
}
return employee;
}
catch (Exception)
{
throw;
}
finally
{
if (conn != null)
{
if (conn.State == ConnectionState.Open)
conn.Close();
conn.Dispose();
}
}
}
This FindEmpById I call in DeleteEmpById function
public void DeleteEmpById(int Key)
{
SqlConnection Con = db.DbConnection;
var x = FindEmpById(Key);
if (x != null)
{
if (Con.State != ConnectionState.Open)
{
Con.Open();
}
SqlCommand cmd = new SqlCommand("sp_DeleteById", Con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#EmpId", Key);
cmd.ExecuteNonQuery();
}
}
FindEmpById disposes the connection conn.Dispose();. So, when you try to use it afterwards, it is not valid any more.
Don't try to reuse a connection. Create a new connection each time you need one. Internally the physical connections are pooled automatically, i.e., the same physical connection will be reused when possible. Creating a new connection with new SqlConnection is lightweight.
Instead of doing
SqlConnection conn = db.DbConnection; // WRONG!
// All your try catch finally and testing for conn.State --- WRONG!
do
// OK!
using (SqlConnection conn = db.CreateConnection()) {
conn.Open();
...
} // Automatically closed and disposed here.
Where CreateConnection creates a new SqlConnection at each call. This is also much easier and straight forward. Change your DbContext class to
public class DbContext
{
private static readonly string _connectionString;
static DbContext()
{
_connectionString = ConfigurationManager.ConnectionStrings["CTXDB"].ConnectionString;
}
public SqlConnection CreateConnection()
{
return new SqlConnection(_connectionString);
}
}
Your overhauled FindEmpById method becomes
public Employee FindEmpById(int key)
{
using (SqlConnection conn = db.CreateConnection())
using (SqlCommand cmd = new SqlCommand("Sp_GetEmployeeById", conn)) {
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#EmpId", key);
var employee = new Employee();
conn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader()) {
if (rdr.Read()) {
employee.Emp_Id = Convert.ToInt32(rdr["Emp_Id"]);
employee.EmpName = rdr["EmpName"].ToString();
employee.Email = rdr["Email"].ToString();
employee.Psw = rdr["Psw"].ToString();
}
}
return employee;
}
}
Btw: You don't need to call FindEmpById(Key) before deleting. Just delete. It is not an error to delete 0 records in SQL.
public void DeleteEmpById(int Key)
{
using (SqlConnection conn = db.CreateConnection())
using (SqlCommand cmd = new SqlCommand("sp_DeleteById", conn)) {
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#EmpId", Key);
conn.Open();
cmd.ExecuteNonQuery();
}
}
i want to show list of SemesterID based on StudentID as shown below.
in comboBox1 i have listed StudentIDs but give me an Errors:
Error #1
Con is not closed.
Error #2
DataReader is Already in use...
public void bindStudentID()
{
try
{
ArrayList a = new ArrayList();
con.Open();
SqlCommand cmd = new SqlCommand("SELECT studentId FROM tbStudent", con);
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
a.Add(dr["studentId"]);
}
comboBox1.DataSource = a;
ArrayList aa = new ArrayList();
SqlCommand cmdd = new SqlCommand("SELECT SemesterID FROM tbSemester Where StudentID='" + comboBox1.Text + "'", con);
SqlDataReader drr = cmd.ExecuteReader();
while (drr.Read())
{
aa.Add(drr["SemesterID"]);
comboBox2.DataSource = aa;
}
con.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
}
}
You forgot to close the reader like this:
// Call Close when done reading.
reader.Close();
try using this syntax for your connection, it's safer:
using (SqlConnection connection =
new SqlConnection(connectionString))
{
SqlCommand command =
new SqlCommand(queryString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
// Call Read before accessing data.
while (reader.Read())
{
ReadSingleRow((IDataRecord)reader);
}
// Call Close when done reading.
reader.Close();
}
I think this code will work fine. This code is explains itself. You were creating a SqlDataReader object without disposing previous one.
public void bindStudentID()
{
try
{
ArrayList a = new ArrayList();
con.Open();
SqlCommand cmd = new SqlCommand("SELECT studentId FROM tbStudent", con);
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
a.Add(dr["studentId"]);
}
comboBox1.DataSource = a;
ArrayList aa = new ArrayList();
SqlCommand cmdd = new SqlCommand("SELECT SemesterID FROM tbSemester Where StudentID='" + comboBox1.Text + "'", con);
dr = null;
dr = cmd.ExecuteReader();
while (dr.Read())
{
aa.Add(drr["SemesterID"]);
}
comboBox2.DataSource = aa;
con.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
}
}
Please mark as answer if I helped
check this,
SqlDataReader drr = cmd.ExecuteReader();
it should be
SqlDataReader drr = cmdd.ExecuteReader();
you are using cmd instead of cmdd command
MSDN says:
You should always call the Close method when you have finished using the DataReader object.
If your Command contains output parameters or return values, they will not be available until the DataReader is closed.
Note that while a DataReader is open, the Connection is in use exclusively by that DataReader. You cannot execute any commands for the Connection, including creating another DataReader, until the original DataReader is closed.
So based on MSDN, try this:
public void bindStudentID()
{
//instantiating a ArrayList object does not need to be included inside Try/Catch block
//try
//{
ArrayList a = new ArrayList();
try
{
//Before opening the connection make sure that connection's current state is not open, otherwise you will get exception
//con.Open();
if (con.State != ConnectionState.Closed)
{
con.Close();
}
con.Open();
SqlCommand cmd = new SqlCommand("SELECT studentId FROM tbStudent", con);
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
a.Add(dr["studentId"]);
}
comboBox1.DataSource = a;
//You should close data reader before using it again or defining new one
dr.Close();
ArrayList aa = new ArrayList();
//No need to define new SqlCommand object and you can use the prev one
cmd = new SqlCommand("SELECT SemesterID FROM tbSemester Where StudentID='" + comboBox1.Text + "'", con);
//No need to define new SqlDataREader object and you can use the prev one
dr = cmd.ExecuteReader();
while (dr.Read())
{
aa.Add(dr["SemesterID"]);
//I think this should be stated out of the while block
//comboBox2.DataSource = aa;
}
comboBox2.DataSource = aa;
//You should close data reader before using it again or defining new one
dr.Close();
//Close the connection in finally block
//con.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
//You should also close the connection even if an exception raised
con.Close();
}
}
I am using the below code to access the MS Access Database. But i got a error message Fill: SelectCommand.Connection property has not been initialized.How can i solve this issue.
common.cs
=========
public static bool DBConnectionStatus()
{
try
{
string conString = #"Provider=Microsoft.Jet.OLEDB.4.0; Data Source=|DataDirectory|db_admin.mdb; Jet OLEDB:Database Password=admin";
using (OleDbConnection conn = new OleDbConnection(conString))
{
conn.Open();
return (conn.State == ConnectionState.Open);
}
}
catch (OleDbException)
{
return false;
}
protected void btn_general_Click(object sender, EventArgs e)
{
try
{
bool state = common.DBConnectionStatus();
if (state == true)
{
cmd = new OleDbCommand("select * from tbl_admin");
da = new OleDbDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds); // Error Here
if (ds.Tables[0].Rows.Count > 0)
{
}
}
}
catch (Exception e1)
{
}
}
I did suggest you to modify your code to something like this:
private DataTable YourData()
{
string conString = #"Provider=Microsoft.Jet.OLEDB.4.0; Data Source=|DataDirectory|db_admin.mdb; Jet OLEDB:Database Password=admin";
DataSet ds = new DataSet();
using (SqlConnection conn = new SqlConnection(conString))
{
try
{
conn.Open();
SqlCommand command = new SqlCommand("select * from tbl_admin", conn);
command.CommandType = CommandType.Text;
SqlDataAdapter adapter = new SqlDataAdapter(command);
adapter.Fill(ds);
if (ds.Tables[0].Rows.Count > 0)
{
// do somethign here
}
}
catch (Exception)
{
/*Handle error*/
}
}
return ds.Tables[0];
}
And then:
protected void btn_general_Click(object sender, EventArgs e)
{
this.YourData(); // you will get the Data here. you can then use it the way you want it
}
You are initializing a Command which you use to construct a DataAdapter, but both without setting the required Connection property:
cmd = new OleDbCommand("select * from tbl_admin"); // <-- no connectuion assigned
da = new OleDbDataAdapter(cmd); // <-- no connectuion assigned
So your exception is pretty self-explanatory.
One final note: using will dispose/close the connection, so the method DBConnectionStatus is pointless. So don't use it, instead use the using in in the first place:
try
{
using(var con = new OleDbConnection(connectionString))
using(var da = new OleDbDataAdapter("elect * from tbl_admin", con))
{
var table = new DataTable();
da.Fill(table); // note that you don't need to open the connection with DataAdapter.Fill
if (table.Rows.Count > 0)
{
// ...
}
}
}
catch (Exception e1)
{
// don't catch an exception if you don't handle it in a useful way(at least loggging)
throw;
}
As per your requirement you can also use SqlDataAdapter instand of ExecuteReader.
public void ReadMyData(string connectionString)
{
string queryString = "SELECT OrderID, CustomerID FROM Orders";
using (OleDbConnection connection = new OleDbConnection(connectionString))
{
OleDbCommand command = new OleDbCommand(queryString, connection);
connection.Open();
OleDbDataReader reader = command.ExecuteReader();
while (reader.Read())
{
Console.WriteLine(reader.GetInt32(0) + ", " + reader.GetString(1));
}
// always call Close when done reading.
reader.Close();
}
}
I created the below method which i tested and does return the correct data. Where I am confused is what is the proper way to populate individual textboxes on a form with the results from this method?
Rather than using an objectdatasource and then binding a gridview to the objectdatasource that works but I need more freedom to customize the form.
public MemberDetails GetMemberDetail(int membershipgen)
{
SqlConnection con = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand("usp_getmemberdetail", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#MEMBERSHIPGEN", SqlDbType.Int, 5));
cmd.Parameters["#MEMBERSHIPGEN"].Value = membershipgen;
try
{
con.Open();
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow);
reader.Read();
MemberDetails mem = new MemberDetails((int)reader["MEMBERSHIPGEN"], (string)reader["MEMBERSHIPID"], (string)reader["LASTNAME"],
(string)reader["FIRSTNAME"], (string)reader["SUFFIX"], (string)reader["MEMBERTYPESCODE"]);
reader.Close();
return mem;
}
catch (SqlException err)
{
throw new ApplicationException("Data error.");
}
finally
{
con.Close();
}
Something along the lines of:
var memberDetails = GetMemberDetail(12345);
textBox1.Text = memberDetails.Prop1;
textBox2.Text = memberDetails.Prop2;
...
Also I would refactor this method and make sure that I properly dispose disposable resources by wrapping them in using statements to avoid leaking unmanaged handles:
public MemberDetails GetMemberDetail(int membershipgen)
{
using (SqlConnection con = new SqlConnection(connectionString))
using (SqlCommand cmd = con.CreateCommand())
{
con.Open();
cmd.CommandText = "usp_getmemberdetail";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#MEMBERSHIPGEN", membershipgen);
using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow))
{
if (reader.Read())
{
return new MemberDetails(
reader.GetInt32(reader.GetOrdinal("MEMBERSHIPGEN")),
reader.GetString(reader.GetOrdinal("MEMBERSHIPID")),
reader.GetString(reader.GetOrdinal("LASTNAME")),
reader.GetString(reader.GetOrdinal("FIRSTNAME")),
reader.GetString(reader.GetOrdinal("SUFFIX")),
reader.GetString(reader.GetOrdinal("MEMBERTYPESCODE"))
);
}
return null;
}
}
}
Get the MemberDetails;
var memberDetails = GetMemberDetail(1);
Populate the textbox;
TextBox.Text = memberDetails.Property;
Jawaid outside of the correct answers that were provided below I would also set SqlConnection con = null; and
SqlCommand cmd = null; outside the try and inside the try put the following
con = new SqlConnection(connectionString);
this way if there is an Error when doing cmd.Parameters.Add -- you can trap that exception
also dispose of the reader object
if (reader != null)
{
((IDisposable)reader).Dispose();
// somthing like that .. do the same for con and cmd objects or wrap them in a using() {}
}
cmd = new SqlCommand("usp_getmemberdetail", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#MEMBERSHIPGEN", SqlDbType.Int, 5));
cmd.Parameters["#MEMBERSHIPGEN"].Value = membershipgen;
I know how to connect, open, read, close as you see below. I also have awesome tutorial how to add update/delete etc.
I can connect dataTable to sql using asp.net controls, but I want to learn how to manipulate it from C#.
MasterCust is my gridview table name. How do I connect the to it?
protected void Page_Load(object sender, EventArgs e)
{
SqlConnection Conn = new SqlConnection("Data Source=aserver;Initial Catalog=KennyCust;Persist Security Info=True;user id=sa;pwd=qwerty01");
SqlDataReader rdr = null;
string commandString = "SELECT * FROM MainDB";
try
{
Conn.Open();
SqlCommand Cmd = new SqlCommand(commandString, Conn);
rdr = Cmd.ExecuteReader();
while (rdr.Read())
{
Console.WriteLine(rdr[0]);
}
}
finally
{
if (rdr != null)
{
rdr.Close();
}
if (Conn != null)
{
Conn.Close();
}
}
//MasterCust.
//MasterCust.DataSource = commandString;
//MasterCust.DataBind();
}
Edit: This code worked
try
{
Conn.Open();
SqlCommand Cmd = new SqlCommand(commandString, Conn);
SqlDataAdapter sdp = new SqlDataAdapter(Cmd);
DataSet ds = new DataSet();
sdp.Fill(ds);
//rdr = Cmd.ExecuteReader();
MasterCust.DataSource = ds.Tables[0];
MasterCust.DataBind();
}
Set the GridView's Datasource property and simply call the DataBind method.
This code will work. ( Tested)
SqlConnection Conn = new SqlConnection("Data Source=Localhost\\SQLEXPRESS;Initial Catalog=Flash2;Integrated Security=True;");
SqlDataReader rdr = null;
string commandString = "SELECT * FROM USER_MASTER";
try
{
Conn.Open();
SqlCommand Cmd = new SqlCommand(commandString, Conn);
rdr = Cmd.ExecuteReader();
MasterCustView.DataSource = rdr;
MasterCustView.DataBind();
}
catch (Exception ex)
{
// Log error
}
finally
{
if (rdr != null)
{
rdr.Close();
}
if (Conn != null)
{
Conn.Close();
}
}