I have this method:
public static int ExecuteNonQuery(ref DbCommand command, out string ErrorMessage)
{
ErrorMessage = String.Empty;
DbTransaction tran = null;
DbConnection conn = null;
int result = 0;
This method is used when accessed on another page. I pass a command in the form of SQLcommand to which I pass a query, but I don't get what this out string is for. This is someone else's code, but I want to use it for abstraction purposes of insertion queries. How should I get the errormessage parameter as it says it's an out string.
I want to do like this on one of my pages:
string Email = "example#example.com";
string Password = "Hello";
SqlCommand command = new SqlCommand("insert into J_example_Tbl(J_example_Email,J_example_Password) VALUES('"+Email+"','"+Password+"')");
PrimaryDBClass.ExecuteNonQuery(command, "Should I do like this");
Regarding
but I don't get what this out string is for
Out is stands for pointing to a variable. The out keyword describes parameters whose actual variable locations are copied onto the stack of the called method, where those same locations can be rewritten. This means that the calling method will access to the changed parameter.
For example: If you declare a variable named test in class1 and want to change the value from another class, class2, and still want to get the changed value from class1 you have to send the test variable with the out keyword from class1 to class2.
Which means in your method:
public static int ExecuteNonQuery(ref DbCommand command, out string ErrorMessage)
If any change occurred in variable ErrorMessage it will point to the actual variable where from it comes. So you will get the changed value from outside of the method ExecuteNonQuery().
The out parameter can be used like this:
string Email = "example#example.com";
string Password = "Hello";
SqlCommand command = new SqlCommand("insert into J_example_Tbl(J_example_Email,J_example_Password) VALUES('"+Email+"','"+Password+"')");
string error;
PrimaryDBClass.ExecuteNonQuery(command, out error);
if(error != null)
....
You should probably check for the result in some other way (like checking the return value or something). Look at Jon Skeet's suggestions as well.
Related
For this code row
bool rowsAffected = CustDAL.AddNewCust(ref newCustomer);
I get an error:
An object reference is required for the non-static field, method, or property
'CustDAL.AddNewCust(ref Customer)
And the code in the CustDAL looks like
public bool AddNewCust(ref Models.Customer newCustomer)
{
SqlCommand command = new SqlCommand
{
CommandText = string.Format("INSERT INTO CUSTOMER VALUES('{0}', '{1}', '{2}', '{3}','{4}')", newCustomer.Title, newCustomer.Forename, newCustomer.Surname, newCustomer.Address, newCustomer.PhoneNumber)
};
return ExecuteNonQuery(command);
}
This code is terrible on a few different levels.
First, it's an open door for SQL Injection attacks.
If anyone would set the customer's Title to, say, Title');DROP TABLE CUSTUMER;--, care to guess what happens next?
Never concatenate strings with user input to create SQL statement. Instead, use parameterized queries to send the user input to the database safely.
A correct insert statement looks like this:
INSERT INTO TableName(Column1, Column2....Columnn) VALUES(#Param1, #Param2...#Paramn)
Second, if you add or remove a column from the Customer table, your code will break. Always specify the columns list when writing insert statements.
Imaging adding a new column to the Customer table for middle name. If you do that, your current statement will break, since the number of columns will stop matching the number of values.
Third, If any of the string properties of newCustomer contains the ' char you'll get an exception - since your SQL would become something like this:
INSERT INTO CUSTOMER VALUES('My title isn't that smart', 'My forename',...
Fourth, you are passing an instance of SqlCommand around, you don't dispose it.
While it's not terrible not to dispose SqlCommand since it does not hold any unmanaged resources, that is an implementation detail. The fact is that it's implementing the IDisposable interface, and as such should be disposed. You can read a bit more about that on this SO post. best practice usage of instances that implements the IDisposable interface is as a local variable inside a using statment -
using(var cmd = new SqlCommand(sql, con))
{
//... do command stuff here
}
Fifth, DbCommand.ExecuteNonQuery returns an int for a reason.
The return value of the ExecuteNonQuery is indicating the number of rows effected by the SQL Statement.
If you use a query that tries to insert multiple records, or update multiple records, you might need to know how may records where actually inserted or updated as a result.
Returning bool hides that data from the calling code.
Sixth, No point of passing the newCustomer parameter by reference, unless the method initializes a new instance of Models.Customer and assign it to the newCustomer reference.
So a better implementation of your AddNewCust method would probably look more like this:
public int AddNewCust(ref Models.Customer newCustomer)
{
var sql = "INSERT INTO CUSTOMER (Title, Forename, Surname, Address, PhoneNumber) VALUES (#Title, #Forename, #Surname, #Address, #PhoneNumber)";
using(var command = new SqlCommand(sql))
{
command.Parameters.Add("#Title", SqlDbType.VarChar).Value = newCustomer.Title;
command.Parameters.Add("#Forename", SqlDbType.VarChar).Value = newCustomer.Forename;
command.Parameters.Add("#Surname", SqlDbType.VarChar).Value = newCustomer.Surname;
// fill in the rest of the parameters here...
return ExecuteNonQuery(command); // change this method to return int...
};
}
All that being said, the cause of the current exception you get is because you try to execute an instance member as if it was a static member - I'm guessing CustDAL is the name of the class, and not the name of a reference to an instance of that class.
For more information, read this SO post
You should first create an instance of the CustDAL class and only then you can access it's non-static members. Since DAL classes should probably be used throughout the application, you should probably have a field containing that reference in the calling class:
private CustDAL _custDal;
// in the constructor:
_custDal = new CustDAL();
// when you want to add a new customer:
var rowsAffected = _custDal.AddNewCust(newCustomer);
How would I take info stored in a Select method and transfer it to a string? I'm trying to get the max value from the match_id column and get its value from command.CommandText into the matchCode string. Where would I go from here?
string connectString = "Server=myServer;Database=myDB;Uid=myUser;Pwd=myPass;";
string matchCode = "";
MySqlConnection connect = new MySqlConnection(connectString);
MySqlCommand command = connect.CreateCommand();
command.CommandText = "SELECT MAX(VAL(match_id)) FROM `data`";
try
{
connect.Open();
command.ExecuteNonQuery();
matchCode = "??";
connect.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
I'm new to C#, as it's like my fourth day trying it out. Thanks for the help!
The ExecuteNonQuery() method is for INSERT/UPDATE/DELETE queries. If you're just getting a single value back, use ExecuteScalar(). If you're getting a whole result set back, use ExecuteReader() or Fill() a DataSet object.
Also, there are some things that are idiomatic to C# that you should be doing:
public int GetMatchCode()
{
//this could be loaded from config file or other source
string connectString = "Server=myServer;Database=myDB;Uid=myUser;Pwd=myPass;";
string sql = "SELECT MAX(VAL(match_id)) FROM `data`";
using (var connect = new MySqlConnection(connectString))
using (var command = new MySqlCommand(sql, connect))
{
connect.Open();
var result = command.ExecuteScalar();
if (result == DBNull.Value)
{
//what you do here depends on your application
// if it's impossible for the query to return NULL, you can even skip this
}
return (int)result;
}
}
Some of the changes need explanation:
I don't ever call .Close(). The using block takes care of that for me, even if an exception was thrown. The old code would have left the connection hanging if an exception occured.
.Net developers tend to believe in very small methods. More than that, this method ought to be part of a class that has nothing but other simple public data access methods and maybe a few private helper methods or properties for abstracting common code in the class.
There is no exception handling code here. If you have small methods that are part of a generic database access class, exception handling should be at higher level, where you are better positioned to make decisions about how to proceed.
So I recently learned that I should absolutely be using parametrized query's to avoid security issues such as SQL injection. That's all fine and all, I got it working.
This code shows some of the code how I do it:
param1 = new SqlParameter();
param1.ParameterName = "#username";
param1.Value = username.Text;
cmd = new SqlCommand(str, sqlConn);
cmd.Parameters.Add(param1);
//and so on
But the problem is, I have over 14 variables that needs to be saved to the db, it's like a registration form. And it would look really messy if I have to write those lines 14 times to parametrize each variable. Is there a more dynamic way of doing this? Like using a for loop or something and parametrizing every variable in the loop somehow?
Use single line SqlParameterCollection.AddWithValue Method
cmd.Parameters.AddWithValue("#username",username.Text);
or other variation you might try like this
command.Parameters.Add(new SqlParameter("Name", dogName));
Here you go... via dapper:
connextion.Execute(sql, new {
username = username.Text,
id = 123, // theses are all invented, obviously
foo = "abc",
when = DateTime.UtcNow
});
that maps to ExecuteNonQuery, but there are other methods, such as Query<T> (binds the data very efficiently by name into objects of type T per row), Query (like Query<T>, but uses dynamic), and a few others (binding multiple grids or multiple objects, etc). All ridiculously optimized (IL-level meta-programming) to be as fast as possible.
Another technique, you can use..
List<SqlParameter> lstPrm = new List<SqlParameter>();
lstPrm.Add(new SqlParameter("#pusername", usernameValue ));
lstPrm.Add(new SqlParameter("#pID", someidValue));
lstPrm.Add(new SqlParameter("#pPassword", passwordValue));
Add the end you can iterate to insert the parameters in your command object
Use my SqlBuilder class. It lets you write paramaterized queries without ever creating a parameter, or having to worry about what its called. Your code will look like this...
var bldr = new SqlBuilder( myCommand );
bldr.Append("SELECT * FROM CUSTOMERS WHERE ID = ").Value(myId);
//or
bldr.Append("SELECT * FROM CUSTOMERS NAME LIKE ").FuzzyValue(myName);
myCommand.CommandText = bldr.ToString();
Your code will be shorter and much more readable. Compared to concatenated queries, you don't even need extra lines. The class you need is here...
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
public class SqlBuilder
{
private StringBuilder _rq;
private SqlCommand _cmd;
private int _seq;
public SqlBuilder(SqlCommand cmd)
{
_rq = new StringBuilder();
_cmd = cmd;
_seq = 0;
}
public SqlBuilder Append(String str)
{
_rq.Append(str);
return this;
}
public SqlBuilder Value(Object value)
{
string paramName = "#SqlBuilderParam" + _seq++;
_rq.Append(paramName);
_cmd.Parameters.AddWithValue(paramName, value);
return this;
}
public SqlBuilder FuzzyValue(Object value)
{
string paramName = "#SqlBuilderParam" + _seq++;
_rq.Append("'%' + " + paramName + " + '%'");
_cmd.Parameters.AddWithValue(paramName, value);
return this;
}
public override string ToString()
{
return _rq.ToString();
}
}
Better still, use my shiny new Visual Studio extension. You declare your parameters in your sql, intact in its own file. My extension will run your query when you save your file, and will make you a wrapper class to call at runtime, and a results class to access your results, with intellisense all over da place. You will see your sql parameters as arguments to the Execute() methods of the wrapper class. You will never have to write another line of parameter code in C#, or reader code, or cmd, or even connection (unless you want to manage that yourself). Gone gone gone :-)
I'm taking data that is in a List of Record objects and putting their contents in to a database:
// Processes a Record and adds it to the database
public bool addRecord(SqlConnection db, List<Record> recordsToAdd)
{
using (SqlCommand command = db.CreateCommand())
{
foreach (Record record in recordsToAdd)
{
// Set the query command text
command.CommandText = #"INSERT INTO SMDGROUP_STPRODMASTER (PRODCODE, TOTFREE, TOTPHYS, ITEMTYPE, PRODESC) VALUES ('#PRODCODE', '#TOTFREE', '#TOTPHYS', '#ITEMTYPE', '#PRODESC')";
SqlParameter param1 = new SqlParameter("#CURSTAT", record.curstat);
SqlParameter param2 = new SqlParameter("#ITEMDESC", record.itemdesc);
SqlParameter param3 = new SqlParameter("#PRODCODE", record.prodcode);
SqlParameter param4 = new SqlParameter("#TOTFREE", record.totfree);
SqlParameter param5 = new SqlParameter("#TOTPHYS", record.totphys);
SqlParameter param6 = new SqlParameter("#ITEMTYPE", record.itemtype);
SqlParameter param7 = new SqlParameter("#PRODESC", record.proddesc);
command.Parameters.Add(param1);
command.Parameters.Add(param2);
command.Parameters.Add(param3);
command.Parameters.Add(param4);
command.Parameters.Add(param5);
command.Parameters.Add(param6);
command.Parameters.Add(param7);
// Execute the query
command.ExecuteNonQuery();
}
return true;
}
}
Here's my Record class:
class Record
{
public string curstat { get; set; }
public string itemtype { get; set; }
public string itemdesc { get; set; }
public string prodcode { get; set; }
public string proddesc { get; set; }
public string totfree { get; set; }
public string totphys { get; set; }
}
Just from looking at the code, I've got a feeling that there is a shorter way of achieving this.
But secondly, I'm not even sure if I've done it correctly that the #PARAMETER values are being replaced.
If I view the contents of command, it still shows the query string with the # parameters.
Also, I'm getting this error on command.ExecuteNonQuery():
String or binary data would be truncated.
The statement has been terminated.
So, my questions are:
Is there a shorter way to set and add multiple parameters to the query?
What could be causing the error?
You have a bigger constructor:
command.Parameters.Add(
"#CategoryName", SqlDbType.VarChar, 80).Value = "toasters";
Using the method AddWithValue will make the code a little bit shorter:
command.Parameters.AddWithValue("#CURSTAT", record.curstat);
//...
I do it a bit differntly.
I have both a extension method and a static method to create SqlParameters.
public static SqlParameter ToParam(this object v,string name){
return new SqlParameter(name,v);
}
Then I do something like this:
var p = new List<SqlParameter>();
p.Add(record.curstat.ToParam("#curstat"));
p.Add(record.itemdesc.ToParam("#itemdesc"));
//etc...
command.Parameters.AddRange(p.ToList());
The String or binary data would be truncated. most likely means your putting too many characters into one of your VARCHAR fields. I.e., if your column PRODDESC is a VARCHAR(50), and the string you're trying to insert is 70 characters, you will see that error.
Others have addressed alternative ways of doing the parameters so you can reduce the lines of code.
For a shorter syntax, you can use AddRange method of the SqlParameterCollection class. It means:
command.Parameters.AddRange(new [] {
new SqlParameter(...),
new SqlParameter(...),
new SqlParameter(...) });
The error you're getting indicates that a string value doesn't fit in the table column or parameter, and is being truncated. You should check the length of the column in comparison to the data being inserted, or specify the length of parameters using another overload of the SqlParameter constructor.
If you wanted to use the following class:
Class MyParam
{
public string name {get;set;}
public object value {get;set;}
}
then you could have a List called myParams and do:
foreach(var p in myParams) command.Parameters.AddWithValue(p.name, p.value);
You obviously have to link the parameters and values somehow and there's no way around that. But if you do it in a class like this then the code that actually does the action is only one line long.
I think the message
String or binary data would be truncated.
The statement has been terminated.
comes from an error in your Command Text: in the SQL Query the parameters, even if they are strings, do not need to be quoted.
Replace the command with this
command.CommandText = #"INSERT INTO SMDGROUP_STPRODMASTER
(PRODCODE, TOTFREE, TOTPHYS, ITEMTYPE, PRODESC)
VALUES (#PRODCODE, #TOTFREE, #TOTPHYS, #ITEMTYPE, #PRODESC)";
To shorten the code i think you could add somewhere (for example in your record class or in an helper class) a method that creates an array of parameter from a record object and then call the AddRange function. It should keeps this function cleaner and you could use it also in other part of you code.
In regards to the error, it's a truncation problem i.e. the length of your parameter is longer than what your column can hold. To resolve this, be more specific when passing your parameters e.g. new SqlParameter("#MyParameter", SqlDbType.VarChar, 30).
Personally I don't think their is anything wrong with how your currently adding the parameters it's readable and does the job. If, however, you want to reduce the amont of lines of code in your function you could either go with what #Royi has suggested or simply package up the parameter adding into another method.
This worked for me:
comando.Parameters.Add(
"#EmailAddress", SqlDbType.VarChar).Value = EmailAddress;
How do i convert a Request.Query string to an integer value. I've tried all the Convert.ToInt32 and Int32.Parse but it says Input string is not in the correct format. I am using the string value as an input to a stored procedure which takes in only integer types for that field.
Here's a part of the code-
string rid=Request.QueryString["RID"];
lblRID.Text = rid;
int id= Int32.Parse(rid);
if (lblRID.Text!= null)
SqlCommand myCommand = new SqlCommand("usp_NewResource_get", myConnection);
myCommand.Parameters.AddWithValue("#RID",id); //RID is int in database and stored procedure
myCommand.CommandType = CommandType.StoredProcedure;
int foo;
int.TryParse(Request.QueryString["foo"], out foo);
or just like you say, int.Parse should convert to int
Could you post some code here ?
The answer thesedays varies based on what framework you're using as msft has made query params part of the view attribute model now for binding (ref: https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.fromqueryattribute?view=aspnetcore-3.1).
You can still access most things via httpcontext for the sake of example.
var fooIsInt = int.TryParse(HttpContext.Request.Query["foo"], out var foo);
Original Example for webforms in .net 2.0
Quick and dirty (and in a page load because this is an example, you should be able to work out what's happening from this)
<script runat="server">
private void Page_Load(object sender, System.EventArgs e){
string test = Request.QueryString["foo"];
//Convert the query string you captured to an int and store it in an int.
int intTest = Convert.ToInt32(test);
Response.Write(intTest.GetType() + "<br>" + intTest);
}
</script>
How about looking on the input that you provide to Int32.Parse?
We use a base class from which every Page inherits. We have the following method that returns integer values from querystring params:
protected int FetchQueryStringIdentifierByKey(string key)
{
int identifier;
var isInt = int.TryParse(Request.QueryString[key], out identifier);
return (isInt) ? identifier : 0;
}
Credit goes to Jayson Knight for his research into his original bench-marking results under .NET 2.0.
string id = Request.QueryString["RID"].ToString();
userid=Convert.ToInt32(id);
How about using TryParse like this:
string rid = Request.QueryString["RID"];
int id = 0;
if (!string.IsNullOrEmpty(rid) && Int32.TryParse(rid, out id))
{
lblRID.Text = rid;
SqlCommand myCommand = new SqlCommand("usp_NewResource_get", myConnection);
myCommand.Parameters.AddWithValue("#RID",id);
myCommand.CommandType = CommandType.StoredProcedure;
myCommand.Execute...
}
The problem is that the value passed in the query string with the RID parameter is not a number, so it cant be parsed to an int. E.g you have ?RID=hello or maybe no RID parameter at all (in which case the value is null). Inspect the querystring to see the actual value passed.