I\'ve come from Java experience and am trying to start with C#. I\'ve read SqlConnection SqlCommand SqlDataReader IDisposable and I can understand that the best practice to
MS SQL server manages connections in it's own connection pool, and they aren't actually disposed. But they are closed so you minimize the network traffic and release the available connections to your server.
Also you should note that if you are using the Linq-To-SQL, the data context will not release the connection until being disposed, so I suggest you just use already working code and do not try to optimize it by yourself.
As VMAtm has said, .net Pools the connections on it's own, so it is perfectly ok to recreate them. As such I generally write a wrapper for the whole process like this one.
public static void RunWithOpenSqlConnection(string connectionString, Action<SqlConnection> connectionCallBack)
{
SqlConnection conn = null;
try
{
conn = new SqlConnection(connectionString);
connectionCallBack(conn);
}
catch (Exception ex)
{
//Log Error Here
}
finally
{
if (conn != null)
conn.Dispose(); //will close the connection
}
}
public static void ExecuteSqlDataReader(string connectionString, string sqlCommand, Action<SqlDataReader> readerCallBack)
{
RunWithOpenSqlConnection(connectionString, delegate(SqlConnection conn)
{
SqlCommand cmd = null;
SqlDataReader reader = null;
try
{
cmd = new SqlCommand(sqlCommand, conn);
reader = cmd.ExecuteReader();
readerCallBack(reader);
}
catch (Exception ex)
{
//Log Error Here
}
finally
{
if (reader != null)
reader.Dispose();
if (cmd != null)
cmd.Dispose();
}
});
}
//Example calling these
ExecuteSqlDataReader(ConfigurationManager.ConnectionStrings["myConnectionString"].ConnectionString, "Select EmployeeID FROM Employees;", delegate(SqlDataReader reader)
{
List<string> employeeIds = new List<string>();
if (reader.HasRows)
{
while(reader.Read())
{
employeeIds.Add((string)reader[0]);
}
}
});
To answer your specific question, you can reuse a SqlConnection
for each query. Just make sure to close your current query (SqlDataReader
, etc.) before you run another one, ie. wrap them in their own using
blocks.
Creating a new instance of the class SqlConnection
does not create a new network connection to SQL Server, but leases an existing connection (or creates a new one). .NET handles the physical connection pooling for you.
When you have finished with your connection (through which you can send multiple queries) just Close()
or Dispose()
(or use a using{}
block preferably).
There is no need, and not good practise, to cache instances of the SqlConnection
class.
Yes, you can create a global SqlConnection instance. In my case I use the SqlConnection as member of my DataContext that I access via Singleton.
public class DatabaseDataContext : DataContext
{
private static DatabaseDataContext instance;
private SqlConnection sqlConnection;
private SqlTransaction sqlTransaction;
//...
public static DatabaseDataContext Instance
{
get
{
return instance ?? (instance = new DatabaseDataContext(connectionString));
}
set
{
instance = value;
}
}
}
You can encapsulate your transactions by closing and opening this connection, i.e.:
DatabaseDataContext.Instance.sqlConnection.Open();
// your transactions...
sqlConnection.Close();
Or you might leave the connection open, but instead specifically begin and end transactions:
DatabaseDataContext.Instance.sqlConnection.Open();
sqlTransaction = sqlConnection.BeginTransaction("Example Insert users");
try{
// ...your first transaction
sqlTransaction.Commit();
}
catch{sqlTransaction.Rollback();}
sqlTransaction = sqlConnection.BeginTransaction("Update baked breads");
try{
// ...your second transaction
sqlTransaction.Commit();
}
catch{sqlTransaction.Rollback();}
// Close the connection at some point
sqlConnection.Close();