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();
}
}
Related
I am Creating WinForm application using C# and SqlServer. I have to handle many database CRUD Queries on it. And also there are so many forms and so many controllers.
Now I want to know is, If i create common class for handle database connectivity with many methods for open connection, close connection, execute Sql command or do any other data retrievals. This method is good or bad?
or below method for run every query is good or bad?
using (SqlConnection connection = new SqlConnection("Integrated Security=SSPI;Initial Catalog=MYDB"))
{
connection.Open();
// Pool A is created.
}
which method is better for performance and security?
Here are some points to think about when using a connection.
1) Dispose the connection object as soon as you no longer need it by using the using statement:
using (var conn = new SqlConnection(connectionstring))
{
// your sql magic goes here
}
2) If you're not disposing the object immediately, you can make sure the connection is closed using a try-finally statement:
var conn = new SqlConnection(connectionstring);
try
{
// do sql shizzle
}
finally
{
conn.Close();
}
3) To prevent SQL injection, use parameterized queries, never concatenated strings
using (var conn = new SqlConnection(connectionstring))
{
conn.Open();
using(var comm = new SqlCommand("select * from FooBar where foo = #foo", conn))
{
comm.Parameters.Add(new SqlParameter("#foo", "bar"));
// also possible:
// comm.Parameters.AddWithValue("#foo", "bar");
using(var reader = comm.ExecuteReader())
{
// Do stuff with the reader;
}
}
}
4) If you're performing multiple update, insert or delete statements, and they all need to be succesful at once, use a transaction:
using (var conn = new SqlConnection(connectionstring))
{
conn.Open();
using(var trans = conn.BeginTransaction())
{
try
{
using(var comm = new SqlCommand("delete from FooBar where fooId = #foo", conn, trans))
{
comm.Parameters.Add(new SqlParameter { ParameterName = "#foo", DbType = System.Data.DbType.Int32 });
for(int i = 0; i < 10 ; i++)
{
comm.Parameters["#foo"].Value = i;
comm.ExecuteNonQuery();
}
}
trans.Commit();
}
catch (Exception exe)
{
trans.Rollback();
// do some logging
}
}
}
5) Stored procedures are used similarly:
using (var conn = new SqlConnection(connectionstring))
{
conn.Open();
using (var comm = new SqlCommand("FooBarProcedure", conn) { CommandType = CommandType.StoredProcedure })
{
comm.Parameters.Add(new SqlParameter("#FooBar", "shizzle"));
comm.ExecuteNonQuery();
}
}
(Source stored procedures: this Answer)
Multi threading: The safest way to use multi threading and SQL connections is to always close and dispose your connection object. It's the behavior the SqlConnection was designed for. (Source: Answer John Skeet)
Best practice is make a common DBHelper class and create CRUD methods into that class.
I am adding code snippet.This may help you.
web.config
<connectionStrings>
<add name="mssqltips"
connectionString="data source=localhost;initial catalog=mssqltips;Integrated Security=SSPI;"
providerName="System.Data.SqlClient" />
</connectionStrings>
DBHelper.cs
//Opening Connection
public SqlConnection GetConnection(string connectionName)
{
string cnstr = ConfigurationManager.ConnectionStrings[connectionName].ConnectionString;
SqlConnection cn = new SqlConnection(cnstr);
cn.Open();
return cn;
}
//for select
public DataSet ExecuteQuery(
string connectionName,
string storedProcName,
Dictionary<string, sqlparameter=""> procParameters
)
{
DataSet ds = new DataSet();
using(SqlConnection cn = GetConnection(connectionName))
{
using(SqlCommand cmd = cn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = storedProcName;
// assign parameters passed in to the command
foreach (var procParameter in procParameters)
{
cmd.Parameters.Add(procParameter.Value);
}
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
{
da.Fill(ds);
}
}
}
return ds;
}
//for insert,update,delete
public int ExecuteCommand(
string connectionName,
string storedProcName,
Dictionary<string, SqlParameter> procParameters
)
{
int rc;
using (SqlConnection cn = GetConnection(connectionName))
{
// create a SQL command to execute the stored procedure
using (SqlCommand cmd = cn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = storedProcName;
// assign parameters passed in to the command
foreach (var procParameter in procParameters)
{
cmd.Parameters.Add(procParameter.Value);
}
rc = cmd.ExecuteNonQuery();
}
}
return rc;
}
If you do not want to dispose context every time you can create repository class and inject SqlConnection inside.
using (SqlConnection connection = new SqlConnection("Integrated Security=SSPI;Initial Catalog=MYDB"))
{
repository.SetConnection(connection);
var values = repository.GetSomething();
}
And Create Class:
public Class Repository
{
private SqlConnection _connection {get; set;}
public void SetConnection(SetConnection connection)
{
_connection = connection;
}
public string GetSomething()
{
_connection.Open();
//do stuff with _connection
_connection.Close();
}
}
Anyway I recommend you to read about ORM's (Entity Framework or Dapper) and SQL injection attack.
I have an object below:
public class DatabaseAccess
{
private static string sConnStr;
private static SqlConnection sqlConn;
private static string ConnectionString
{
get
{
if (String.IsNullOrEmpty(sConnStr))
{
sConnStr = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
}
return sConnStr;
}
}
public static int OpenConnection
{
get
{
sqlConn = new SqlConnection(ConnectionString);
return 0;
}
}
public static SqlConnection Connection
{
get
{
if (sqlConn.State != ConnectionState.Open)
{
sqlConn = new SqlConnection(ConnectionString);
sqlConn.Open();
}
return sqlConn;
}
}
}
So whenever I need a connection in my web application, I use something like:
DataTable dt = new DataTable();
using (SqlConnection cnn = DatabaseAccess.Connection)
{
using (SqlDataAdapter da = new SqlDataAdapter("MyAStoredProcedure", cnn))
{
da.SelectCommand.CommandType = CommandType.StoredProcedure;
da.Fill(dt);
}
}
return dt;
It all seems well except when there are 2 users running the codes at the same time, I will get in my web application the following error:
There is already an open DataReader associated with this Command needs to be Closed.
I need some advice how do I resolve the above issue?
Thank you.
That's because you're sharing connection objects - don't do that. DatabaseAccess.Connection should create a new SqlConnection every time.
Try to create a new instance of SqlConnection in the using statement
DataTable dt = new DataTable();
using (SqlConnection cnn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString))
{
cnn.Open();
using (SqlDataAdapter da = new SqlDataAdapter("MyAStoredProcedure", cnn))
{
da.SelectCommand.CommandType = CommandType.StoredProcedure;
da.Fill(dt);
}
}
return dt;
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 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.
This bit of code runs on Windows Compact Framework and what it does is obvious. It looks as it should be refactored (especially considering that I may want to add cmd.ExecuteResultSet() later), but I can't see an elegant way to do it. Any ideas appreciated.
internal void RunNonQuery(string query)
{
string connString = GetLocalConnectionString();
using (SqlCeConnection cn = new SqlCeConnection(connString))
{
cn.Open();
SqlCeCommand cmd = cn.CreateCommand();
cmd.CommandText = query;
cmd.ExecuteNonQuery();
}
}
internal int RunScalar(string query)
{
string connString = GetLocalConnectionString();
using (SqlCeConnection cn = new SqlCeConnection(connString))
{
cn.Open();
SqlCeCommand cmd = cn.CreateCommand();
cmd.CommandText = query;
return int.Parse(cmd.ExecuteScalar().ToString());
}
}
I'm not sure I would refactor it, but perhaps:
static void PerformQuery(string connectionString, string command,
Action<SqlCeCommand> action)
{ //TODO: sanity checks...
using(SqlCeConnection conn = new SqlCeConnection(connectionString))
using(SqlCeCommand cmd = conn.CreateCommand()) {
cmd.CommandText = command;
conn.Open();
action(cmd);
}
}
internal void RunNonQuery(string query)
{
string connString = GetLocalConnectionString();
PerformQuery(connString, query, cmd => cmd.ExecuteNonQuery());
}
internal int RunScalar(string query)
{
int result = 0;
string connString = GetLocalConnectionString();
PerformQuery(connString, query,
cmd => {result = int.Parse(cmd.ExecuteScalar().ToString()); }
);
return result;
}
Otherwise - just maybe a CreateAndOpenConnection(string) method, and a CreateCommand(SqlCeConnection,string) method.
If you are using C# 3.0, you could do something like the following:
private T CreateCommand<T>(string query, Func<SqlCeCommand, T> func)
{
var connString = GetLocalConnectionString();
using (var cn = new SqlCeConnection(connString))
{
cn.Open();
using (var cmd = cn.CreateCommand())
{
cmd.CommandText = query;
return func(cmd);
}
}
}
private void CreateCommand(string query, Action<SqlCeCommand> action)
{
CreateCommand<object>(query, cmd =>
{
action(cmd);
return null;
});
}
internal void RunNonQuery(string query)
{
CreateCommand(query, cmd => cmd.ExecuteNonQuery());
}
internal int RunScalar(string query)
{
return CreateCommand(query, cmd =>
int.Parse(cmd.ExecuteScalar().ToString()));
}
I would create a class out of the code to wrap the connection creation and command execution logic. This will provide you with a single place to implement transactions in the future and will consolidate creation of the connection and command. This consolidation will allow for settings timeouts, joining transactions, etc.
class Connection : IDisposable
{
readonly SqlConnection _conn;
public Connection()
{
string connString = GetLocalConnectionString();
_conn = new SqlConnection(connString);
_conn.Open();
}
public void Dispose() { _conn.Dispose(); }
public SqlCommand CreateCommand(string qry)
{
SqlCommand cmd = _conn.CreateCommand();
cmd.CommandText = qry;
//cmd.CommandTimeout = TimeSpan.FromMinutes(x);
return cmd;
}
public int ExecuteNonQuery(string qry)
{
using (SqlCommand cmd = CreateCommand(qry))
return cmd.ExecuteNonQuery();
}
public int RunScalar(string qry)
{
using (SqlCommand cmd = CreateCommand(qry))
return int.Parse(cmd.ExecuteScalar().ToString());
}
}
Then if you still want to maintain your original API, you do the following:
class SqlCode
{
internal void RunNonQuery(string query)
{
using (Connection cn = new Connection())
cn.ExecuteNonQuery(query);
}
internal int RunScalar(string query)
{
using (Connection cn = new Connection())
return cn.RunScalar(query);
}
}
The only thing left is to re-insert the 'Ce' in the SqlXxxx stuff ;)