问题
I've got the following code which makes a connection to a db > runs a stored proc > and then moves on.
I believe it is easy to get db programming wrong so it is important to be defensive: is the following defensive? (or can it be improved?)
public int RunStoredProc()
{
SqlConnection conn = null;
SqlCommand dataCommand = null;
SqlParameter param = null;
int myOutputValue;
try
{
conn = new SqlConnection(ConfigurationManager.ConnectionStrings["IMS"].ConnectionString);
conn.Open();
dataCommand = conn.CreateCommand();
dataCommand.CommandType = CommandType.StoredProcedure;
dataCommand.CommandText = "pr_blahblah";
dataCommand.CommandTimeout = 200; //seconds
param = new SqlParameter();
param = dataCommand.Parameters.Add("@NumRowsReturned", SqlDbType.Int);
param.Direction = ParameterDirection.Output;
dataCommand.ExecuteNonQuery();
myOutputValue = (int)param.Value;
return myOutputValue;
}
catch (SqlException ex)
{
MessageBox.Show("Error:" + ex.Number.ToString(), "Error StoredProcedure");
return 0;
}
finally
{
if (conn != null)
{
conn.Close();
conn.Dispose();
}
}
}
CODE NOW LOOKS LIKE THE FOLLOWING
I've tried to use all the help offered by everyone and the above code has now been amended to the following which I hope is now sufficiently defensive:
public SqlConnection CreateConnection()
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["IMS"].ConnectionString);
return conn;
}
public int RunStoredProc()
{
using (var conn = CreateConnection())
using (var dataCommand = conn.CreateCommand())
{
conn.Open();
dataCommand.CommandType = CommandType.StoredProcedure;
dataCommand.CommandText = "pr_BankingChargebacks";
dataCommand.CommandTimeout = 200; //5 minutes
SqlParameter param = new SqlParameter();
param = dataCommand.Parameters.Add("@NumRowsReturned", SqlDbType.Int);
param.Direction = ParameterDirection.Output;
dataCommand.ExecuteNonQuery();
int myOutputValue = (int)param.Value;
return myOutputValue;
}
}
回答1:
Try using, well, the using construct for such things.
using(var conn = new SqlConnection(ConfigurationManager.ConnectionStrings["IMS"].ConnectionString)
{
}
Once you do that, I think you will be at the right level of "defense". Similarly try to do the same for anything that has to be disposed ( like the command)
回答2:
- There is no need to call both
.Close()and.Dispose() - Prefer the
usingblock instead oftry-finally - Dispose of the command object
- I would remove the catch clause. It doesn't belong here (though YMMV).
If you are going to write this code all over the place, stop. At least create a small helper class to do this, or use a light-weight 'ORM' like Massive, Dapper or PetaPoco. For an example of an ADO.Net helper class, see https://github.com/jhgbrt/yadal/blob/master/Net.Code.ADONet.SingleFile/Db.cs.
回答3:
The main thing I would notice is a MessageBox in database-access code. I can't think of a single scenario that is useful. Just let the exception rise. Don't catch that.
As a general template:
using(var conn = CreateConnection())
using(var cmd = conn.CreateCommand())
{
// setup cmd and the parameters
conn.Open();
cmd.ExecuteNonQuery();
// post-process cmd parameters (out/return/etc)
}
Note: no Close(), no catch; all the finally are handled by the using. Much simpler; much harder to get wrong.
Another thing to emphasise is the use of a factory method for creating the connection; don't put:
new SqlConnection(ConfigurationManager.ConnectionStrings["IMS"].ConnectionString)
into every method; after all... that could change, and it is unnecessary repetition.
回答4:
If MessageBox.Show("Error:" + ex.Number.ToString(), "Error StoredProcedure"); is how you're going to handle an exception then you're not logging or even retrieving the actual exception details.
回答5:
In addition to manojlds's advice I recommend that you make yourself some reusable helper methods to call the database. For example, make yourself a method that reads the connection string, creates the connection and opens it. Don't repeat infrastructure stuff everywhere.
You can do the same for invoking an sproc or a command text.
来源:https://stackoverflow.com/questions/11687146/is-this-defensive-code-or-are-there-possibilities-that-it-could-encouter-problem