C#: Creating SQLParameter at runtime - c#

I'm curious if it's possible to create a SQLParameter in runtime and add it to SQLCommand or not.
What I'm trying to do is:
public void addParameters(string paramName, paramType, SqlParameter paramCommand)
{
SqlParameter myParameter = new SqlParameter("#" + paramName, paramType);
paramCommand.Parameters.Add(myParameter);
}
The problem is passing paramType, it's SqlDbType and I don't know:
1.how to implement/use it here
2.when to calling this function.
NOTE: I just want to add parameters to my SQLCommand object. It's something like this:
SqlParameter myParameter = new SqlParameter("#user", SqlDbType.Char);

Something like this:
public static class CommandExtensions
{
public static void AddParameter(this IDbCommand command, string name, object value)
{
if (command == null) throw new ArgumentNullException("command");
if (name == null) throw new ArgumentNullException("name");
var p = command.CreateParameter();
p.ParameterName = name;
p.Value = value ?? DBNull.Value;
command.Parameters.Add(p);
}
}
which allows you to:
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT * FROM Users WHERE FirstName LIKE #name";
command.AddParameter("name", "J%");
// [...]
}
That code is also independent of the ADO.NET driver and can therefore be reused in most projects that use ADO.NET. IF you want more information about writing reusable ADO.NET code, read my article: http://blog.gauffin.org/2013/01/ado-net-the-right-way/
The problem is passing paramType, it's SqlDbType and I don't know
Typically you do not have to specify the db type.

Your approach is correct:
cmd.Parameters.Add(new SqlParameter("#Name", "Frank Rizzo"));
One thing to note is that SqlParameter constructor provides a number of overloads, making creating of a separate function to return new SqlParameter largely unnecessary.

In addition to jgauffin's answer:
When constructing an SqlParameter, if you are not sure about the SqlDbType of the parameter, you can use the SqlParameter constructor which only takes, parameter name and value as parameters. Namely; SqlParameter Constructor (String, Object).
In order to MSDN documentation,
When you specify an Object in the value parameter, the SqlDbType is
inferred from the Microsoft .NET Framework type of the Object.

There are a number of ways to add SQL parameters in ADO.NET. Generally it is not necessary to provide a SQL database type as data is sent as a string and implicitly converted by SQL server.
MSDN quote the following examples-
command.Parameters.Add("#ID", SqlDbType.Int);
command.Parameters["#ID"].Value = customerID;
// Use AddWithValue to assign Demographics.
// SQL Server will implicitly convert strings into XML.
command.Parameters.AddWithValue("#demographics", demoXml);
The first example could also be written as-
command.Parameters.Add("#ID", SqlDbType.Int).Value = customerID;
See http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.parameters(v=vs.100).aspx for more.
Obviously providing data which does not match the SQL data type will cause a SQL exception, you should set length restrictions and validate input, converting it to a compatible type locally using the "as" syntax can be useful as can regular expressions.
jgauffin's answer could easily be adapted to use the syntax that includes the DB type but I'm not sure what the advantage is (except as a reminder to you) as you get no type warnings regardless of values assigned, or even a exception before an attempt to execute the command.

Related

SQLDataReader not returning rows/populating list

I'm trying to return all rows of a single column in my database to populate a list. When I execute the stored procedure in SQL, it works fine, but nothing gets returned when I try to do it in C#.
public static List<string> GetRows(string filter_one, string filter_two)
{
var retrievedRows = new List<string>();
var storedProc = "dbo.MyStoredProc";
using (SqlConnection connection = new SqlConnection(MY_CONNECTION_STRING))
using (SqlCommand command = new SqlCommand(storedProc, connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("#FilterOne", SqlDbType.VarChar).Value = filter_one;
command.Parameters.Add("#FilterTwo", SqlDbType.VarChar).Value = filter_two;
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
retrievedRows.Add(reader.GetString(0));
}
}
}
return retrievedRows;
}
Any ideas? I get no errors in the console or when I run it on IIS Express either. When I watch retrievedRows, the size stays at 0 even though when I run the same query in SQL with the same passed parameters, it returns results for me.
EDIT: Please excuse me, my brain must be running a bit slow today. One of the parameters I was passing was pointed at the (empty) value of the wrong webcontrol. I don't know how I missed this.
There is only one issue with your posted snippet that I can see, which could pose a problem:
command.Parameters.Add("#FilterOne", SqlDbType.VarChar).Value = filter_one;
command.Parameters.Add("#FilterTwo", SqlDbType.VarChar).Value = filter_two;
In this section, you're adding two VARCHAR parameters but not specifying a length for them. Try changing your code to add a length specification:
var filterOne = new SqlParameter("FilterOne", System.Data.SqlDbType.VarChar, 50);
The constructor in use here is SqlParameter(string, SqlDbType, int):
Parameters
-
parameterName (String): The name of the parameter to map.
dbType (SqlDbType): One of the SqlDbType values.
size (Int32): The length of the parameter.
When working with VARCHAR you must specify a length or anything outside of the default length (which is 1 byte for definitions and variables, and 30 bytes for CAST and CONVERT) will be truncated:
When n isn't specified in a data definition or variable declaration statement, the default length is 1. If n isn't specified when using the CAST and CONVERT functions, the default length is 30.

Conversion failed when converting from string to uniqueidentifier - loading DataTable with ExecuteReader

In attempting to do a SQL query (which returns one string and one uniqueidentifier to columns 0 and 1 respectively) I get "Conversion failed when converting from a character string to uniqueidentifier" in my exceptions log. How can I avoid this? I'm assuming the issue is, the datatables columns are not defined, so it's expecting a string and SQL is trying to convert it. The exception is logged. Surprisingly to me the GUID is stored successfully to da[1]. So my program technically works, however I want to clear this exception and to do that I need to understand why it's happening and how to go about fixing it.
da = new DataTable();
da.Clear();
...
string invoiceStatusSQL = #"select status,invoice_id from invoices where acct_id='" + accountid + "'";
command = new SqlCommand(invoiceStatusSQL, cnn);
da.Load(command.ExecuteReader());
You should always parameterise your SQL queries to help prevent SQL injection and avoid problems like you're facing now. See Why do we always prefer using parameters in SQL statements?.
Use SqlParameter to add the parameters to the SqlCommand.
string invoiceStatusSQL = #"select status, invoice_id from invoices where acct_id = #accountId";
command = new SqlCommand(invoiceStatusSQL, cnn);
SqlParameter idParam = new SqlParameter("#accountId", accountid);
command.Parameters.Add(idParam);
da.Load(command.ExecuteReader());
You can also specify the actual database type when creating the parameter, which will reduce any issues you might have with the framework inferring the type incorrectly (although I don't think that would happen in your case for a Guid/UniqueIdentifier). One way to specify the type is shown below.
var p = new SqlParameter
{
ParameterName = "#accountId",
SqlDbType = SqlDbType.UniqueIdentifier,
Value = accountid
};

Unicode SQL Query W/ Parameter instead N Prefix

I have an insert query to execute from within a C# against a SQL Server database.
The column I am inserting to is of type nvarchar.
the data I am inserting to that column is non-english.
Is it sufficient for me to use AddWithValue in order to pass the non-english data to the server? like this example:
string dogName = "עברית";
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand("INSERT INTO Dogs1(Name) VALUES #Name", connection))
{
command.Parameters.AddWithValue("Name", dogName);
command.ExecuteNonQuery();
}
}
Or must I use the N prefix to declare it unicode? like it says so here.
If I am understanding the question correctly, you can explicitly set the SqlCommand parameter to be a specific data type. You will be able to set it to be nvarchar as shown by the following link: http://msdn.microsoft.com/en-us/library/yy6y35y8.aspx
This below code snippet is taken directly from MSDN:
SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "#CategoryName";
parameter.SqlDbType = SqlDbType.NVarChar;
parameter.Direction = ParameterDirection.Input;
parameter.Value = categoryName;
This uses an explicitly created SqlParameter instance, but it is the same idea by indexing the SqlParameterCollection of the SqlCommand instance.
I believe the link at the bottom is only really talking about values within SQL itself.
As far as I'm aware, the code you've got should be absolutely fine - otherwise there'd be no way of specifying Unicode text.
Of course, it's probably worth validating this - but I'd be very surprised if it didn't work.

C# Run a procedure without specifying a parameter name

How can I execute a stored procedure that takes in parameters without having to specify the prameters name? The name of the parameter in the stored procedure may change from CustomerID to CustID so I don't want to have to keep changing my code.
Rather than doing what is provided below where you specify the parameter name -
command.Parameters.Add("#dtStart", SqlDbType.DateTime);
command.Parameters["#dtStart"].Value = startDate;
command.Parameters.Add("#CustomerID", SqlDbType.NChar);
command.Parameters["#CustomerID"].Value = customerID;
I am looking to do something like this -
command.Parameters.Add(startDate, customerID);
The name of the parameter in the stored procedure may change from CustomerID to CustID
Slap the person who does that.
Parameter names are your reliable way of identifying a parameter. The other option is sequence, seems a lot more flaky.
I don't think you can create a SqlParameter object without specifying its name. However, you should be able to use the DeriveParameters method (see MSDN) to get a collection of parameters with the names automatically retreived from the SQL server.
You can find an example here. It looks roughly like this:
SqlCommand command = // create a command for calling the stored procedure
SqlCommandBuilder.DeriveParameters(command);
// Now you can set values of parameters in a loop
for(int i = 0; i < command.Parameters.Length; i++) {
var parameter = command.Parameters[i]
// Set value of ith parameter
}
You can create a nameless SQL parameter if you force its name to null or empty after it's been added to the Parameters collection, something like this:
var par = cmd.CreateParameter();
par.Value = myValue;
cmd.Parameters.Add(par); // this will change the name to "ParameterX"
par.ParameterName = null;
Use Parameter Discovery, scroll down on: http://msdn.microsoft.com/en-us/library/ff664692(PandP.50).aspx
Using Unnamed parameters is only possible with OdbcCommand and OleDbCommand object parameters.
You could use SQL's exec, which does not ask for parameter names:
command.CommandText = string.Format(
"exec dbo.YourProcedure {0}, '{1}'",
intParameter,
stringParameter.Replace("'","''")
);
command.ExecuteNonQuery();
If your parameter source is untrustworthy, be sure to escape single quotes in string parameters. It's done for stringParameter in the snippet above.

Executing a stored procedure yields an error saying it's expecting a parameter that I have supplied?

I've written a piece of c# code which executes a stored procedure on an MS SQL box. I Create the SP like so:
SqlCommand command = new SqlCommand(creditLimitRequestSP, connection);
command.CommandType = System.Data.CommandType.StoredProcedure;
I then create a set of parameters using the constructor for the parameters:
SqlParameter p1 = new SqlParameter(p1, p1Value);
vdnParam.Size = 7;
Some of my parameters are integers in which case I parse them like so:
int p4Value = 0;
bool parsedP4 = Int32.TryParse(p4AsAString, out p4Value);
SqlParameter p4;
if (parsedP4)
{
p4 = new SqlParameter("#p4", SqlDbType.Int, p4Value);
p4.Size = sizeof(Int32);
}
else
{
throw new InvalidFieldDataException("p4", p4AsString);
}
Then after I've parsed the parameters from a dictionary I add them like so:
command.Parameters.Add(p1);
command.Parameters.Add(p2);
command.Parameters.Add(p3);
command.Parameters.Add(p4);
command.Parameters.Add(p5);
command.Parameters.Add(p6);
When I execute the stored procedure I get an exception telling me is is expecting a parameter which I have added to the command. Why would it do this?
Initially I thought this might be because the parameters were being added in the wrong order but I changed them to match the order in the Stored procedure (event though it shouldn't matter as I'm using named parameters) and it didn't resolve anything.
The parameter in question does not have a default value in the SP, and it an integer value.
If the exception you get is telling that you are not adding a parameter, then it must be that you're not adding it. The parameters must match by name indeed (order doesn't matter), and all non-optional parameters must be present. Direction (Input, Output) must also be correctly set.
The exception message should tell you which parameter is not present, or has the wrong type.
There is no much to help you here, since there must be something at odd between what you believe you're passing and what you actually pass. You should monitor the RPC:Starting Event Class in the SQL Profiler and see exactly what are you passing.
I think I've found the answer...
p4 = new SqlParameter("#p4", SqlDbType.Int, p4Value);
The third Parameter is not the value it's the size of the param.
To set the value you need to call;
p4.Value = p4Value;
Going to test and will come back...

Categories

Resources