I want to implement a simple delete button for my database. The event method looks something like this:
private void btnDeleteUser_Click(object sender, EventArgs e)
{
if (MessageBox.Show("Are you sure?", "delete users",MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK)
{
command = new SqlCommand();
try
{
User.connection.Open();
command.Connection = User.connection;
command.CommandText = "DELETE FROM tbl_Users WHERE userID = #id";
int flag;
foreach (DataGridViewRow row in dgvUsers.SelectedRows)
{
int selectedIndex = row.Index;
int rowUserID = int.Parse(dgvUsers[0,selectedIndex].Value.ToString());
command.Parameters.AddWithValue("#id", rowUserID);
flag = command.ExecuteNonQuery();
if (flag == 1) { MessageBox.Show("Success!"); }
dgvUsers.Rows.Remove(row);
}
}
catch (SqlException ex)
{
MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
}
finally
{
if (ConnectionState.Open.Equals(User.connection.State))
User.connection.Close();
}
}
else
{
return;
}
}
but I get this message:
A variable #id has been declared. Variable names must be unique within
a query batch or stored procedure.
Is there any way to reuse this variable?
Parameters.AddWithValue adds a new Parameter to the command. Since you're doing that in a loop with the same name, you're getting the exception "Variable names must be unique".
So you only need one parameter, add it before the loop and change only it's value in the loop.
command.CommandText = "DELETE FROM tbl_Users WHERE userID = #id";
command.Parameters.Add("#id", SqlDbType.Int);
int flag;
foreach (DataGridViewRow row in dgvUsers.SelectedRows)
{
int selectedIndex = row.Index;
int rowUserID = int.Parse(dgvUsers[0,selectedIndex].Value.ToString());
command.Parameters["#id"].Value = rowUserID;
// ...
}
Another way is to use command.Parameters.Clear(); first. Then you can also add the parameter(s) in the loop without creating the same parameter twice.
Rather than:
command.Parameters.AddWithValue("#id", rowUserID);
Use something like:
System.Data.SqlClient.SqlParameter p = new System.Data.SqlClient.SqlParameter();
Outside the foreach, and just set manually inside the loop:
p.ParameterName = "#ID";
p.Value = rowUserID;
I would use this:
public static class DbExtensions
{
public static void AddParameter(SQLiteCommand command, string name, DbType type, object value)
{
var param = new SQLiteParameter(name, type);
param.Value = value;
command.Parameters.Add(param);
}
}
Then, call this:
DbExtensions.AddParameter(command, "#" + fieldOfSearch[i], DbType.String, value[i]);
Error is because you are adding the same parameter again and again in each iteration of the loop.
I would move that code to a seperate method so that i can call it from multiple places as needed.
public bool DeleteUser(int userId)
{
string connString = "your connectionstring";
try
{
using (var conn = new SqlConnection(connString))
{
using (var cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "DELETE FROM tbl_Users WHERE userID = #id";
cmd.Parameters.AddWithValue("#id", userId);
conn.Open();
cmd.ExecuteNonQuery();
return true;
}
}
}
catch(Exception ex)
{
//Log the Error here for Debugging
return false;
}
}
Then call it like this
foreach (DataGridViewRow row in dgvUsers.SelectedRows)
{
int selectedIndex = row.Index;
if(dgvUsers[0,selectedIndex]!=null)
{
int rowUserID = int.Parse(dgvUsers[0,selectedIndex].Value.ToString());
var result=DeleteUser(rowUserID)
}
else
{
//Not able to get the ID. Show error message to user
}
}
Related
I'm populating my List like this:
public List<Functions> ListFunctions(int proj)
{
List<Functions> lFunctions = new List<Functions>();
try
{
string sql = "SELECT id, description FROM functios WHERE project = #project ORDER BY description ASC";
MySqlCommand cmd = new MySqlCommand();
cmd.CommandText = sql;
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add(new MySqlParameter("#project", MySqlDbType.Int32)).Value = proj;
using (MySqlDataReader reader = _dal.ExecutaReader(cmd))
{
while (reader.Read())
{
lFunctions.Add(new Function
{
ID = Convert.ToInt32(reader["id"]),
Descripton = reader["description"].ToString()
});
}
}
return lFunction;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
How may I loop through the list and get those values now ? The Description and the ID
I'd like to know a way to get exactly the fieldName, like:
string name = ListFunction["FieldName"].ToString()
You just enumerate over the returned list. For example:
var functions=ListFunctions(0); // For example
foreach(var function in functions)
{
Console.WriteLine("{0} = {1}",function.ID, function.Description);
}
when you select items from the listbox, you want to delete selecteditems. Why doesnt it work when selected data removed from database? I must have missed something. I got error message
No mapping exists from object type.
This is a method parameter:
IsDelete = _dinnerRemover.RemoveDinners(lstDinner.SelectedItems);
This class is to delete data from database
public bool RemoveDinners(dynamic dinnerItems)
{
Dinners = new List<FoodInformation>();
using (var sqlConn = new SqlConnection(_sqlConnectionString))
{
const string sqlQuery = "delete from DinnerTemplates where Dinner = #dinner";
using (var command = new SqlCommand(sqlQuery, sqlConn))
{
try
{
//command.CommandType = CommandType.StoredProcedure;
//command.CommandText = "sp_dinner";
foreach (var item in dinnerItems)
{
command.CommandType = CommandType.Text;
command.Parameters.AddWithValue("#dinner", item);
command.ExecuteNonQuery();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
sqlConn.Close();
}
}
}
return Dinners;
}
If dinnerItems is a list of strings then say that, don't use dynamic unless you absolutely have to.
To delete a bunch of items, issue one sql query with an IN clause. Don't issue lots of individual queries.
Try this:
public int RemoveDinners(List<string> dinnerItems)
{
using (var sqlConn = new SqlConnection(_sqlConnectionString))
{
const string sqlQuery = "delete from DinnerTemplates where Dinner in ({0})";
using (var command = new SqlCommand())
{
var paramNames = new string[dinnerItems.Count];
int i = 0;
foreach (string item in dinnerItems)
{
string paramName = "#Dinner" + i;
command.Parameters.AddWithValue(paramName, item);
paramNames[i] = paramName;
i += 1;
}
command.CommandText = String.Format(sqlQuery, String.Join(",", paramNames));
command.Connection = sqlConn;
command.CommandType = CommandType.Text;
sqlConn.Open();
return command.ExecuteNonQuery();
}
}
}
You have to bear in mind that you kind of left out some really relevant code, like what is a DinnerItem, since you're getting the error on a line related to its type.
However, the reason you're getting that error is because item can't be marshaled to a type of something like string or int.
That's probably because item is likely a custom class. One option would be to override the ToString method of the class:
public override string ToString() {
// return some property value, or set of property values
// strung together here.
}
another option would be to send in the actual Property you want off of item when issuing AddWithValue.
You need to define SqlDbType for command's parameter.
don't use dynamic type,use string..
if i were you,i would rather
IsDelete = _dinnerRemover.RemoveDinners(lstDinner.SelectedItems.ToString());
change the parameter to :
public bool RemoveDinners(string dinnerItems)
and the query to :
const string sqlQuery = "delete from DinnerTemplates where Dinner = dinnerItems";
When calling the same query method twice in a session of the app, I get "DBCommandExcept"
As an experiment, I decided to dispose of the connection object at the end of the method, to see if that was the problem.
I no longer get the DBCommandExcept err msg, but instead get, "the connectionstring property has not been initialized."
IOW, it's sort of a Catch-22 situation at the moment. The pertinent code is:
string query = "SELECT Bla FROM Blah";
SqlCeCommand cmd = new SqlCeCommand(query);
cmd.CommandType = CommandType.Text;
SqlCeConnection conn = dbconn.GetConnection();
cmd.CommandType = CommandType.Text;//probably unnecessary
cmd.Connection = conn;
SqlCeDataReader myReader = cmd.ExecuteReader(CommandBehavior.SingleRow);
try
{
if (myReader.Read())
{
itemID = myReader.GetString(ITEMID_INDEX);
packSize = myReader.GetString(PACKSIZE_INDEX);
recordFound = true;
}
}
catch (Exception ex)
{
RRDR.LogMsgs.Append(string.Format("Exception in PopulateControlsIfVendorItemsFound(): {0}", ex.Message));
}
finally
{
myReader.Close();
//if (null != conn)
//{
// conn.Dispose();
//}
}
// Re: the commented-out block above: When it is active, the DBCommandExcept problem is not seen; however, I then get, "the connectionstring property has not been initialized"
I think the only non-SQL-CE-standard bit above is the dbConn.GetConnection(). Here's some of that code:
SqlCeConnection objCon = null;
. . .
public SqlCeConnection GetConnection()
{
return objCon;
}
private DBConnection() // class constructor
{
try
{
. . .
objCon = new SqlCeConnection(conStr);
objCon.Open();
. . .
Again, the error (either one, whichever one I "choose" to have) is seen only the second time through this method during one run of the app. The first time works fine.
UPDATE
I added the code below, and the comments tell the tale of woe:
// With conn check only, still get two consecutive DBCommandExcepts
// With cmd check only, still get two consecutive DBCommandExcepts
// With both, still get two consecutive DBCommandExcepts; IOW, all have the same effect
if (null != conn)
{
conn.Close();
}
if (null != cmd)
{
cmd.Dispose();
}
UPDATE 2
Based on unicron's suggestion, I tried using "using."
In two of the three cases (SqlCeCommand and SqlCeDataReader), converting to "using" made no diff; in the other one (SqlCeConnection), it raised the err msgs, "The ConnectionString property has not been initialized."
Still, though, the code is cleaner with the two usings, so thanks for that nudge in the best practices direction.
Here's what it looks like now:
private bool PopulateControlsIfPlatypusItemsFound()
{
const int ITEMID_INDEX = 0;
const int PACKSIZE_INDEX = 1;
bool recordFound = false;
try
{
string PlatypusId = txtPlatypus.Text.ToString().Trim();
string PlatypusItemId = txtUPC.Text.ToString().Trim();
string itemID = string.Empty;
string packSize = string.Empty;
string query = string.Format("SELECT ItemID, PackSize FROM PlatypusItems WHERE PlatypusID = {0} AND PlatypusItemID = {1}", PlatypusId, PlatypusItemId);
using (SqlCeCommand cmd = new SqlCeCommand(query))
{
cmd.CommandType = CommandType.Text;
SqlCeConnection conn = dbconn.GetConnection();
if ((null != conn) && (!conn.State.Equals(ConnectionState.Open)))
{
conn.Open();
TTBT.LogMsgs.Append("Connection opened");
}
cmd.CommandType = CommandType.Text;//probably unnecessary
cmd.Connection = conn;
using (SqlCeDataReader myReader = cmd.ExecuteReader(CommandBehavior.SingleRow))
{
if (myReader.Read())
{
itemID = myReader.GetString(ITEMID_INDEX);
packSize = myReader.GetString(PACKSIZE_INDEX);
recordFound = true;
}
}
txtID.Text = itemID;
txtSize.Text = packSize;
return recordFound;
}
}
catch (Exception ex)
{
TTBT.LogMsgs.Append(string.Format("Exception in PopulateControlsIfPlatypusItemsFound: {0} - {1}\r\n", ex.Message, ex.InnerException));
return recordFound;
}
}
UPDATE 3
I've come even closer to normalcy by replacing the custom connection code with the generic sort, adding another "using" to the mix:
private bool PopulateControlsIfVendorItemsFound()
{
const int ITEMID_INDEX = 0;
const int PACKSIZE_INDEX = 1;
bool recordFound = false;
DUCKBILL.LogMsgs.Append("Made it into frmEntry.PopulateControlsIfVendorItemsFound()\r\n");
try
{
string vendorId = txtVendor.Text.ToString().Trim();
string vendorItemId = txtUPC.Text.ToString().Trim();
string itemID = string.Empty;
string packSize = string.Empty;
if ( dbconn.isValidTable( "VendorItems" ) == -1 )
{
DUCKBILL.LogMsgs.Append("VendorItems not a valid table");//do not see this msg; good! VendorItems is seen as valid...
return false;
}
string query = string.Format("SELECT ItemID, PackSize FROM VendorItems WHERE VendorID = {0} AND VendorItemID = {1}", vendorId, vendorItemId);
using (SqlCeCommand cmd = new SqlCeCommand(query))
{
cmd.CommandType = CommandType.Text;
using (SqlCeConnection conn = new SqlCeConnection())
{
string filename = "\\badPlace2B\\CCRDB.SDF";
conn.ConnectionString = string.Format("Data Source = {0}", filename);
cmd.CommandType = CommandType.Text;//probably unnecessary/moot
cmd.Connection = conn;
conn.Open();
using (SqlCeDataReader myReader = cmd.ExecuteReader(CommandBehavior.SingleRow))
{
if (myReader.Read())
{
itemID = myReader.GetString(ITEMID_INDEX);
packSize = myReader.GetString(PACKSIZE_INDEX);
recordFound = true;
}
}
}
txtID.Text = itemID;
txtSize.Text = packSize;
return recordFound;
}
}
catch (Exception ex)
{
DUCKBILL.LogMsgs.Append(string.Format("Exception in PopulateControlsIfVendorItemsFound: {0} - {1}\r\n", ex.Message, ex.InnerException));
return recordFound;
}
}
...yet I still get "DBCommandExcept"...
As to "stop futzing around with opening the connection," isn't it necessary to do so? How could/should the code above be different?
UPDATE 4
What is even more bizarre is that now my debug log file has stopped being written. I have been writing it out both in the global exception handler AND in the main form's Closed event(), and it always has (until now) at least a few entries, but within the last couple of updates to the code, it is no longer being written...????
Both places global exception handler and main form's Closed event(), the code is like so:
public static bool inDebugMode = true;
. . .
if (CCR.inDebugMode)
{
DateTime dt = DateTime.Now;
string timeAsStr = string.Format("{0}_{1}_{2}_{3}.txt", dt.Hour, dt.Minute, dt.Second, dt.Millisecond);
using (StreamWriter file = new StreamWriter(timeAsStr))
{
// If the app closes normally, this is how the file is written; if it doesn't,
// (it crashed) it's written in PDAClient.ExceptionHandler()
file.WriteLine(SSCS.LogMsgs.ToString());
}
}
Since you are making several calls to a database file (that isn't going to change), I'd start out by defining your connection string and your SQL statements at the top of your class as global values:
private const int ITEMID_INDEX = 0;
private const int PACKSIZE_INDEX = 1;
private const string SQL_CONN_STR = "Data Source=\\badPlace2B\\CCRDB.SDF";
private const string SQL_GET_VENDOR_ITEMS = "SELECT ItemID, PackSize " +
"FROM VendorItems " +
"WHERE VendorID=#VendorID AND VendorItemID=#VendorItemID";
These never change, so there is no reason to define them again each time you call your routine.
Personally, I do not like inserting values into SQL statements, like you have shown. Rather, try to use Parameters.
To use Parameters, you'll need to look into your database to see what type of columns VendorID and VendorItemID are. My guess is that they are both int values, but these could be GUID like values, requiring VarChar type strings. If these are strings, you should write down what sizes the columns are defined as.
For example: Below, my Serial_Number column is the SqlDbType.NVarChar and the size is 50. An SqlCeParameter for this column would be:
cmd.Parameters.Add("#Serial_Number", SqlDbType.NVarChar, 50).Value = txtSerial_Number.Text.Trim();
Since I did not know what type of data you use, I created an enumerated type to show how each method would be used. If you do not have access to the table's design, the last resort is "AddWithValue" (I personally hate that one, because it makes me look like I don't know what my database has inside).
enum ParamStyle { AddWithValue, AddIntegers, AddVarChar }
To use this enumerated type, I modified the signature of your method to pass in that value:
private bool PopulateControlsIfVendorItemsFound(ParamStyle style) {
Obviously, you will not need this, because you should know what technique you are going to be coding with.
I wasn't able to figure out what your dbconn object was. Initially, I thought this was your SqlCeConnection, but that does not have an isValidTable method, so I just commented it out:
//if (dbconn.isValidTable("VendorItems") == -1) {
// DUCKBILL.LogMsgs.Append("VendorItems not a valid table");//do not see this msg; good! VendorItems is seen as valid...
// return false;
//}
Speaking of SqlCeConnection...
I combined your SqlCeCommand instance with your SqlCeConnection instance. Less code typically means fewer errors:
using (var cmd = new SqlCeCommand(SQL_GET_VENDOR_ITEMS, new SqlCeConnection(SQL_CONN_STR))) {
The CommandType, by default, is CommandType.Text, so this line is unnecessary:
// cmd.CommandType = CommandType.Text; (this is the default)
I moved most of your variable reading outside of the try/catch routine, as none of that should ever cause an exception to be generated.
Also, I used the more targeted SqlCeException instead of the general Exception. The only thing that could fail in the block is something SqlCe related, and the SqlCeException will give you better/more specific error messages than the general Exception object will.
} catch (SqlCeException err) {
So, what does it look like all put together?
Code:
enum ParamStyle { AddWithValue, AddIntegers, AddVarChar }
private const int ITEMID_INDEX = 0;
private const int PACKSIZE_INDEX = 1;
private const string SQL_CONN_STR = "Data Source=\\badPlace2B\\CCRDB.SDF";
private const string SQL_GET_VENDOR_ITEMS = "SELECT ItemID, PackSize FROM VendorItems WHERE VendorID=#VendorID AND VendorItemID=#VendorItemID";
private bool PopulateControlsIfVendorItemsFound(ParamStyle style) {
bool recordFound = false;
//DUCKBILL.LogMsgs.Append("Made it into frmEntry.PopulateControlsIfVendorItemsFound()\r\n");
string itemID = null;
string packSize = null;
//string vendorId = txtVendor.Text.Trim();
//string vendorItemId = txtUPC.Text.Trim();
//string query = string.Format("SELECT ItemID, PackSize FROM VendorItems WHERE VendorID = {0} AND VendorItemID = {1}", vendorId, vendorItemId);
//if (dbconn.isValidTable("VendorItems") == -1) {
// DUCKBILL.LogMsgs.Append("VendorItems not a valid table");//do not see this msg; good! VendorItems is seen as valid...
// return false;
//}
using (var cmd = new SqlCeCommand(SQL_GET_VENDOR_ITEMS, new SqlCeConnection(SQL_CONN_STR))) {
// cmd.CommandType = CommandType.Text; (this is the default)
if (style == ParamStyle.AddIntegers) { // Adding Integers:
cmd.Parameters.Add("#VendorID", SqlDbType.Int).Value = Convert.ToInt32(txtVendor.Text.Trim());
cmd.Parameters.Add("#VendorItemID", SqlDbType.Int).Value = Convert.ToInt32(txtUPC.Text.Trim());
} else if (style == ParamStyle.AddVarChar) { // Adding String Values
// NOTE: Here, you should look in your database table and
// use the size you defined for your VendorID and VendorItemID columns.
cmd.Parameters.Add("#VendorID", SqlDbType.VarChar, 25).Value = txtVendor.Text.Trim();
cmd.Parameters.Add("#VendorItemID", SqlDbType.VarChar, 50).Value = txtUPC.Text.Trim();
} else if (style == ParamStyle.AddWithValue) { // Adding as Objects (only if you don't know what the data types are)
cmd.Parameters.AddWithValue("#VendorID", txtVendor.Text.Trim());
cmd.Parameters.AddWithValue("#VendorItemID", txtUPC.Text.Trim());
}
try {
cmd.Connection.Open();
using (var myReader = cmd.ExecuteReader(CommandBehavior.SingleRow)) {
if (myReader.Read()) {
itemID = myReader.GetString(ITEMID_INDEX);
packSize = myReader.GetString(PACKSIZE_INDEX);
recordFound = true;
}
}
} catch (SqlCeException err) {
//DUCKBILL.LogMsgs.Append(string.Format("Exception in PopulateControlsIfVendorItemsFound: {0}\r\n", err.Message));
// (I never return from a 'catch' statement) return recordFound;
} finally {
if (cmd.Connection.State == ConnectionState.Open) {
cmd.Connection.Close();
}
}
}
if (recordFound) { // set these last, and set them OUTSIDE of the try/catch block
txtID.Text = itemID;
txtSize.Text = packSize;
}
return recordFound;
}
Happy Coding!
I've got code that creates a table if it doesn't exist and adds all necessary columns, but for cases where the user has an older version of the table, it adds some new
columns. Yet when that second condition is true and the DDL to add columns runs, I get, "A column ID occurred more than once in the specification"
Here is the code, along with the helper functions to determine existence of table and column:
bool tableExists = dbconn.isValidTable(tablename) != -1;
if (!tableExists)
{
ddl = "CREATE TABLE Bla (. . . salvationID nvarchar(19), salvation float, discount float)";
dbconn.DBCommand(ddl, false);
}
else // (the table does exist)
{
if(!dbconn.isValidField(tablename,"redemptionID"))
{
ddl = string.Format("ALTER TABLE {0} ADD redemptionID nvarchar(19) ", tablename);
dbconn.DBCommand(ddl,false);
. . .
public int isValidTable(string tableName)
{
int validTable = -1;
string tblQuery = string.Format("SELECT COUNT(*) FROM {0}", tableName);
checkConnection();
try
{
SqlCeCommand cmd = objCon.CreateCommand();
cmd.CommandText = tblQuery;
object objcnt = cmd.ExecuteScalar();
validTable = Int32.Parse(objcnt.ToString());
}
catch
{
validTable = -1;
}
return validTable;
}
//This has been beautified/elegantized thanks to p.s.w.g at http://stackoverflow.com/questions/15693639/how-can-i-determine-whether-a-column-exists-in-a-sql-server-ce-table-with-c
public bool isValidField(string tableName, string columnName)
{
bool retVal;
string tblQuery = "SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #tableName AND
COLUMN_NAME = #columnName";
checkConnection();
try
{
SqlCeCommand cmd = objCon.CreateCommand();
cmd.CommandText = tblQuery;
SqlCeParameter tblNameParam = new SqlCeParameter("#tableName", SqlDbType.NVarChar, 128);
tblNameParam.Value = tableName;
cmd.Parameters.Add(tblNameParam);
SqlCeParameter colNameParam = new SqlCeParameter("#columnName", SqlDbType.NVarChar, 128);
colNameParam.Value = tableName;
cmd.Parameters.Add(colNameParam);
object objvalid = cmd.ExecuteScalar();
retVal = !Convert.IsDBNull(objvalid);
}
catch
{
retVal = false;
}
return retVal;
}
I suspect what's going on is that isValidField() is throwing an exception and your catch is simply swallowing it while stating the field doesn't exist... when it probably does.
I would highly suggest that instead of just swallowing it that you actually display the message so that you know what's going on.
For example:
public bool isValidField(string tableName, string columnName)
{
bool retVal;
string tblQuery = "SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #tableName AND COLUMN_NAME = #columnName";
checkConnection();
try
{
SqlCeCommand cmd = objCon.CreateCommand();
cmd.CommandText = tblQuery;
SqlCeParameter tblNameParam = new SqlCeParameter("#tableName", SqlDbType.NVarChar, 128);
tblNameParam.Value = tableName;
cmd.Parameters.Add(tblNameParam);
SqlCeParameter colNameParam = new SqlCeParameter("#columnName", SqlDbType.NVarChar, 128);
colNameParam.Value = tableName;
cmd.Parameters.Add(colNameParam);
object objvalid = cmd.ExecuteScalar();
retVal = !Convert.IsDBNull(objvalid);
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
retVal = false; // <-- wrong answer
}
return retVal;
}
Also, that function should not be a boolean. You have 3 conditions: 1. It exists; 2. It doesn't exist; 3. error occurred.
In the event of an error you don't want the later methods to think that it couldn't find it. Also, I'd do the same thing to where you are validating the table exists.
Try checking for DBNull.Value.
try
{
SqlCeCommand cmd = objCon.CreateCommand();
cmd.CommandText = tblQuery;
object objcnt = cmd.ExecuteScalar();
if ((objcnt != null) && (objcnt != DBNull.Value)) {
validTable = Int32.Parse(objcnt.ToString());
} else {
MessageBox.Show("NULL returned from CreateCommand. Remove this line.");
}
}
catch
I have a ugly code that can't be reused. I have many similar queries. I want to rewrite it with SqlParameterCollectionExtensions or other better ways. But I don't know about SqlParameterCollectionExtensions at all.
protected void GridView1_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
SqlConnection con = new SqlConnection(strCon);
con.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandText = "UPDATE Problem_DE SET ProbDesc = #ProbDesc, field_1 = #field_1, field_2 = #field_2, field_3 = #field_3, field_4 = #field_4, field_5 = #field_5, field_6 = #field_6, field_7 = #field_7 WHERE (ProbId = #ProbId)";
if (e.NewValues["ProbDesc"] == null)
cmd.Parameters.AddWithValue("#ProbDesc", DBNull.Value);
else
cmd.Parameters.AddWithValue("#ProbDesc", e.NewValues["ProbDesc"]);
if (e.NewValues["field_1"] == null)
cmd.Parameters.AddWithValue("#field_1", DBNull.Value);
else
cmd.Parameters.AddWithValue("#field_1", e.NewValues["field_1"]);
if (e.NewValues["field_2"] == null)
cmd.Parameters.AddWithValue("#field_2", DBNull.Value);
else
cmd.Parameters.AddWithValue("#field_2", e.NewValues["field_2"]);
if (e.NewValues["field_3"] == null)
cmd.Parameters.AddWithValue("#field_3", DBNull.Value);
else
cmd.Parameters.AddWithValue("#field_3", e.NewValues["field_3"]);
if (e.NewValues["field_4"] == null)
cmd.Parameters.AddWithValue("#field_4", DBNull.Value);
else
cmd.Parameters.AddWithValue("#field_4", e.NewValues["field_4"]);
\\ blah blah
cmd.ExecuteNonQuery();
con.Close();
}
The sql parameters come from e or textbox etc.
Thanks.
Maybe something like this? I assume the problem is that you have a variable number of values, depending on the problem table?
private void UpdateProblem(string problemName, string problemDescription, int problemId, object[] fieldValues)
{
SqlConnection con = null;
SqlCommand cmd = new SqlCommand();
StringBuilder sql = new StringBuilder();
int fieldCounter = 1;
// start building the sql statement
sql.AppendFormat("UPDATE {0} SET ProbDesc = #ProbDesc", problemName);
// add the 'description' parameter
cmd.Parameters.Add(new SqlParameter("#ProbDesc", problemDescription));
// add each field value to the update statement... the SqlParameter will infer the database type.
foreach(object fieldValue in fieldValues)
{
// add additional SET clauses to the statement
sql.AppendFormat(",field{0} = #field{0}", fieldCounter);
// add the field parameter to the command's collection
cmd.Parameters.Add(new SqlParameter(String.Format("#field{0}", fieldCounter), fieldValue));
fieldCounter++;
}
// finish up the SQL statement by adding the where clause
sql.Append(" WHERE (ProbId = #ProbId)");
// add the 'problem ID' parameter to the command's collection
cmd.Parameters.Add(new SqlParameter("#ProbId", problemId));
// finally, execute the SQL
try
{
con.Open();
cmd.Connection = con;
cmd.CommandText = sql.ToString();
cmd.ExecuteNonQuery();
}
catch(SqlException ex)
{
// do some exception handling
}
finally
{
if(con != null)
con.Dispose();
}
}
An example to call this code would be:
public void UpdateProblemDe()
{
int problemId = FetchCurrentProblemId();
string field1 = e.NewValues["field_1"];
string field2 = e.NewValues["field_2"];
string field3 = ddlField3.SelectedValue;
int field4 = Convert.ToInt32(e.NewValues["field_4"]);
string field5 = txtField5.Text;
DateTime field6 = DateTime.Now.AddSeconds(Convert.ToInt32(ddlField6.SelectedValue));
string field7 = txtField7.Text;
object[6] fieldValues;
if(field1 != null)
fieldValues[0] = field1;
else
fieldValues[0] = DBNull.Value;
if(field2 != null)
fieldValues[1] = field2;
else
fieldValues[1] = DBNull.Value
fieldValues[2] = field3;
fieldValues[3] = field4;
fieldValues[4] = field5;
fieldValues[5] = field6;
fieldValues[6] = field7;
UpdateProblem("Problem_DE", "Houston, we have a problem", problemId, fieldValues);
}
The example code above is obviously just a demonstration of how to make an array of objects stored from page control values and doesn't include any data validation that you will need to implement in production code.
If you won't know how big the object[] array needs to be at runtime, you can change it to be a generic List of objects and add the items dynamically.
Based on this answer
Create new SQLCommand's or reuse the same one
you could refactor your code the following way
public class DbHepler
{
private readonly string _connectionString;
public DbHepler(string connectionString)
{
_connectionString = connectionString;
}
public void ExecuteNonQuery(string query)
{
ExecuteNonQuery(query, null);
}
public void ExecuteNonQuery(string query, Dictionary<string, object> parameters)
{
using (SqlConnection conn = new SqlConnection(_connectionString))
{
conn.Open();
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = query;
if (parameters != null)
{
foreach (string parameter in parameters.Keys)
{
cmd.Parameters.AddWithValue(parameter, parameters[parameter] ?? DBNull.Value);
}
}
cmd.ExecuteNonQuery();
}
conn.Close();
}
}
}
your code will look something like:
protected void GridView1_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
string query = "UPDATE Problem_DE SET ProbDesc = #ProbDesc, field_1 = #field_1, field_2 = #field_2, field_3 = #field_3, field_4 = #field_4, field_5 = #field_5, field_6 = #field_6, field_7 = #field_7 WHERE (ProbId = #ProbId)";
Dictionary<string, object> parameters = new Dictionary<string, object>();
if (e.NewValues["ProbDesc"] == null)
parameters.Add("#ProbDesc", null);
else
parameters.Add("#ProbDesc", e.NewValues["ProbDesc"]);
//blah blah
DbHepler dbHepler = new DbHepler("your sql connection info");
dbHepler.ExecuteNonQuery(query, parameters);
}