Exception when AddWithValue parameter is NULL - c#

I have following code for specifying parameters for SQL query. I am getting following exception when I use Code 1; but works fine when I use Code 2. In Code 2 we have a check for null and hence a if..else block.
Exception:
The parameterized query '(#application_ex_id nvarchar(4000))SELECT E.application_ex_id A' expects the parameter '#application_ex_id', which was not supplied.
Code 1:
command.Parameters.AddWithValue("#application_ex_id", logSearch.LogID);
Code 2:
if (logSearch.LogID != null)
{
command.Parameters.AddWithValue("#application_ex_id", logSearch.LogID);
}
else
{
command.Parameters.AddWithValue("#application_ex_id", DBNull.Value );
}
QUESTION
Can you please explain why it is unable to take NULL from logSearch.LogID value in Code 1 (but able to accept DBNull)?
Is there a better code to handle this?
Reference:
Assign null to a SqlParameter
Datatype returned varies based on data in table
Conversion error from database smallint into C# nullable int
What is the point of DBNull?
CODE
public Collection<Log> GetLogs(LogSearch logSearch)
{
Collection<Log> logs = new Collection<Log>();
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
string commandText = #"SELECT *
FROM Application_Ex E
WHERE (E.application_ex_id = #application_ex_id OR #application_ex_id IS NULL)";
using (SqlCommand command = new SqlCommand(commandText, connection))
{
command.CommandType = System.Data.CommandType.Text;
//Parameter value setting
//command.Parameters.AddWithValue("#application_ex_id", logSearch.LogID);
if (logSearch.LogID != null)
{
command.Parameters.AddWithValue("#application_ex_id", logSearch.LogID);
}
else
{
command.Parameters.AddWithValue("#application_ex_id", DBNull.Value );
}
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.HasRows)
{
Collection<Object> entityList = new Collection<Object>();
entityList.Add(new Log());
ArrayList records = EntityDataMappingHelper.SelectRecords(entityList, reader);
for (int i = 0; i < records.Count; i++)
{
Log log = new Log();
Dictionary<string, object> currentRecord = (Dictionary<string, object>)records[i];
EntityDataMappingHelper.FillEntityFromRecord(log, currentRecord);
logs.Add(log);
}
}
//reader.Close();
}
}
}
return logs;
}

Annoying, isn't it.
You can use:
command.Parameters.AddWithValue("#application_ex_id",
((object)logSearch.LogID) ?? DBNull.Value);
Or alternatively, use a tool like "dapper", which will do all that messing for you.
For example:
var data = conn.Query<SomeType>(commandText,
new { application_ex_id = logSearch.LogID }).ToList();
I'm tempted to add a method to dapper to get the IDataReader... not really sure yet whether it is a good idea.

I find it easier to just write an extension method for the SqlParameterCollection that handles null values:
public static SqlParameter AddWithNullableValue(
this SqlParameterCollection collection,
string parameterName,
object value)
{
if(value == null)
return collection.AddWithValue(parameterName, DBNull.Value);
else
return collection.AddWithValue(parameterName, value);
}
Then you just call it like:
sqlCommand.Parameters.AddWithNullableValue(key, value);

Just in case you're doing this while calling a stored procedure: I think it's easier to read if you declare a default value on the parameter and add it only when necessary.
SQL:
DECLARE PROCEDURE myprocedure
#myparameter [int] = NULL
AS BEGIN
C#:
int? myvalue = initMyValue();
if (myvalue.hasValue) cmd.Parameters.AddWithValue("myparamater", myvalue);

some problem, allowed with Necessarily set SQLDbType
command.Parameters.Add("#Name", SqlDbType.NVarChar);
command.Parameters.Value=DBNull.Value
where SqlDbType.NVarChar you type. Necessarily set SQL type.

Related

IF condition check inside USING method and SqlConnection

I am trying to run data validation, execute some code and pass data from one SQL query to another.
My current code looks like the below:
public string SelectUniqueKeyNumber()
{
string newList = string.Join(Environment.NewLine, listOfSkus).ToString();
string key_id;
string sqlConnectionString = #"someConnectionString";
using (SqlConnection connection = new SqlConnection(sqlConnectionString))
{
connection.Open();
SqlCommand command = new SqlCommand("select top 1 KEY_NUMBER from MyTable where QTY_ON_HAND > 0 " + newList + " order by NEWID()", connection);
SqlDataReader readerKey = command.ExecuteReader();
readerKey.Read();
key_id = String.Format(readerKey[0].ToString());
}
SelectSkuNumber(key_id);
return key_id;
}
What I am trying to do is to check if my readerKey.Read() is not returning null value. If it does then stop the process, otherwise continue. I've tried it in the way as shown below:
public string SelectUniqueKeyNumber()
{
string newList = string.Join(Environment.NewLine, listOfSkus).ToString();
string key_id;
string sqlConnectionString = #"someConnectionString";
using (SqlConnection connection = new SqlConnection(sqlConnectionString))
{
connection.Open();
SqlCommand command = new SqlCommand("select top 1 KEY_NUMBER from MyTable where QTY_ON_HAND > 0 " + newList + " order by NEWID()", connection);
SqlDataReader readerKey = command.ExecuteReader();
readerKey.Read();
if(readerkey.Read().ToString() == null)
{
//--- stop processing
}
else
{
key_id = String.Format(readerKey[0].ToString());
}
}
SelectSkuNumber(key_id); //---> Then ...(key_id) is not declared value
return key_id;
}
By doing so, I cannot access and pass data of SelectSkuNumber(key_id) due to: Use of unassigned local variable 'key_id'
Any ideas?
All you need do is assign something to key_id when you declare it, like:
string key_id = null; // not string key_id;
and later, after the using:
if (key_id != null)
{
SelectSkuNumber(key_id); //---> Then ...(key_id) is not declared value
}
return key_id;
The caller of the function should, of course, know what to do if a null is returned.
To avoid that particular problem, you can assign some value or nnull to key_id, eg. key_id = "";.
But you have some more problem there:
You are prone to SQL injection, you should use Parameters collection of SqlCommand class.
Are you sure you are concatenating your query correctly? Let's suppose
newList = {"some", "thing"};
Then your query would be:
select top 1 KEY_NUMBER
from MyTable where QTY_ON_HAND > 0
some
thing
order by NEWID()
Which is very, very incorrect to say the least.
if(readerkey.Read().ToString() == null) condition... Read returns bool, which is either true or false, it isn't reference type, so ToString() will never be null, thus the condition will always fail. If you want to check if there was NULL in database you should check:
if (readerKey.Read() && readerKey["KEY_NUMBER"] == DBNull.Value)
which first read row, then receives value of column in that row. It uses short-circuiting for the case, where no records are returned.
readerKey.Read(); is unnecessary before the if statement.

Stored procedure expects a parameter I am already passing in

I am trying to execute a stored procedure with this declaration:
ALTER PROCEDURE [dbo].[getByName]
#firstName varchar,
#lastName varchar
AS
...
And I am calling in C# as follows:
public List<Person> GetPersonByName(string first, string last)
{
var people = new List<Person>();
var connString = ConfigurationManager.ConnectionStrings["MyDbConnString"].ConnectionString;
using (var conn = new SqlConnection(connString))
{
using (var cmd = new SqlCommand("dbo.getByName",conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#firstName", SqlDbType.VarChar, 50)).Value = first;
cmd.Parameters.Add(new SqlParameter("#lastName", SqlDbType.VarChar, 50)).Value = last;
conn.Open();
using (var reader = cmd.ExecuteReader())
{
people = ReadPeopleData(reader);
}
conn.Close();
}
}
return people;
}
But I just get back this error:
Procedure or function 'getByName' expects parameter '#firstName' which was not supplied.
Update:
ALTER PROCEDURE [dbo].[getEmployeesByName]
#firstName varchar(50),
#lastName varchar(50)
AS
...
and stating:
cmd.Parameters.Add(new SqlParameter("#firstName", SqlDbType.VarChar, 50)).Value
for both parameters, yet it continues to throw the exception.
I have seen this issue occur many, many times in two common scenarios:
The value being assigned to the parameter is null (or Nothing in VB.Net). This is the .Net null, not the DB null (DBNull.Value) and this happens most commonly with strings.
The parameter being created is associated with the wrong command object. This commonly occurs when you have multiple command objects in the same class with similar names.
Please double-check the code to ensure that the string variable is not set to null and that the parameter is being added to the correct command.
Update
Based on the full updated code that was posted, the likely issue is 1 above.
To circumvent this problem in your code, add the following at the top of the method:
if (first == null) {
first = "";
}
if (last == null) {
last = "";
}
Try this it will work:
SqlParameter[] sqlParams = new SqlParameter[] {
new SqlParameter("#UserName",(object)userName ?? DBNull.Value),
new SqlParameter("#Password",(object)password ?? DBNull.Value)
};
If parameter is NULL than replace it with DBNull Type using ?? Operator
Please add CommandaType to SQLCommand.
e.g: scmd.CommandType = CommandType.StoredProcedure;

Suggestions on C# SQL parameters (collection) passed to function?

I would like to build a SQL function that has large amounts of reuse with ExecuteNonQuery but the biggest issue I have are the parameters.
I am not sure what others would do to make this simple and resilient so that I can simply pass the SQL script.
For example SELECT * FROM table WHERE userid = #userid AND isactive = #isactive, and then perhaps the peramiters can be an array.
public void ExecuteSQLCeNonQuery(string _Sql, ?parameter array of some type?)
{
SqlCeConnection sCon = null;
SqlCeCommand sCmd = null;
int countOf = 0;
try
{
sCon = new SqlCeConnection( sqlConnectionStringLocal);
sCmd = new SqlCeCommand(_Sql, sCon);
sCmd.Parameters.AddWithValue("#recBatchDateTarget", sDateOnly); <----- I know this will have to parse the collection some how.
sCon.Open();
countOf = (int)sCmd.ExecuteNonQuery();
sCon.Close();
}
catch (SqlCeException ex)
{
Console.WriteLine("** DEBUG: ExecuteSQLCeNonQuery: {0} - {1}", ex.Message, sSql);
}
finally
{
if (sCmd != null)
sCmd.Dispose();
if (sCon != null)
sCon.Dispose();
}
}
Any suggestions on how to handle the array or collection of parameters?
Just declare it as an IEnumerable<SqlParameter> so the caller can provide any collection he wants (array, List<SqlParameter>, ...)
public void ExecuteSQLCeNonQuery(string _Sql, IEnumerable<SqlParameter> parameters)
{
...
if (parameters != null) // Null check so caller can pass null if there are no parameters
{
foreach(SqlParameter parameter in parameters)
{
cmd.Parameters.Add(parameter);
}
...
}
}
Following the rabbit hole let me to this post. The .AddRange method for the parameters should work.
How to use SqlCeParameterCollection?
You could try to make a separate class for accessing data,
and place your methods there(like delete method,update,insert,etc..),
your connectionstring,if always accessing same database,could be outside the methods.
In your methods parameters you can pass a string with your formatted
sql statements,setting your sqlcommand object to that parameter.
You can make that class static or public(you would then need to
instatiate it in your clientform).
ex:
inside clientform....
string query = "SELECT * from tablename";
sqlaccessclass sqlclass = new sqlaccessclass();
sqlclass.GetAll(query);
Your return type for fetching data methods would be DataSet while in ExecuteNonQuery
methods would be void.

How to read nvarchar(x) into String when null values are allowed?

I have a SQL table with a column of type nvarchar(20) and want to read that column using SqlDataReader. Looks like the only way to do this is to use GetSqlChars() followed by ToSqlString():
String result = reader.GetSqlChars(index).ToSqlString().Value
the problem is that if the stored value happens to be null (and that's valid for my case) I get
[SqlNullValueException: Data is Null. This method or property cannot be called on Null values.]
System.Data.SqlTypes.SqlString.get_Value() +3212527
so I have to first check what the value returned by ToSqlString() returns in IsNull():
SqlString asSqlString = reader.GetSqlChars(index).ToSqlString();
String result = asSqlString.IsNull() ? null : asSqlString.Value;
which works but requires lots of extra code and looks really inelegant.
Is there a more elegant way to achieve the same effect?
Perhaps:
var value = reader.IsDBNull(index) ? null : reader.GetString(index);
Or even shorter:
var value = reader[index] as string;
You can use the GetValue() method:
// define your query
string query = "SELECT YourField FROM dbo.YourTable WHERE ID = 1";
using(SqlConnection conn = new SqlConnection("......"))
using(SqlCommand cmd = new SqlCommand(query, conn))
{
conn.Open();
using(SqlDataReader rdr = cmd.ExecuteReader())
{
if(rdr.Read())
{
string fieldValue = rdr.GetValue(2).ToString();
}
}
conn.Close();
}
GetString method is specifically coded to throw an exception if the database value is null. This is by design.
The docs for GetString state:
Call IsDBNull to check for null values before calling this method
On the other hand, if you use GetValue() and you end up with a DBNull object as your value, the DBNull.ToString method automatically returns String.Empty.

SqlCommand.Parameters.AddWithValue issue: Procedure or function X expects parameter #Y, which was not supplied

I have a problem with the folowwing piece of code. I am passing a parameter (List<SqlParameter>) to a method executing the following code.
When it executes SQL Server throws an error saying that the proc expects a parameter that was not provided. I know this error and understand it, and when stepping through the code I can see that the cmdExecuteReader object has a collection of parameters with the correct name and value. What could be the problem?
public SqlDataReader ExecuteReader(string storedProcedure, List<SqlParameter> parameters = null)
{
SqlCommand cmdExecuteReader = new SqlCommand()
{
CommandType = System.Data.CommandType.Text,
Connection = conn,
CommandText = storedProcedure
};
if (parameters != null)
{
foreach (SqlParameter param in parameters)
{
cmdExecuteReader.Parameters.AddWithValue(param.ParameterName, param.Value);
}
}
if (conn.State == System.Data.ConnectionState.Closed)
conn.Open();
return cmdExecuteReader.ExecuteReader();
}
Is the .Value set to null for any of the parameters? If so, they aren't sent. Try:
cmdExecuteReader.Parameters.AddWithValue(param.ParameterName,
param.Value ?? DBNull.Value);
(note the null-coalescing with DBNull.Value)
Also, note that AddWithValue may impact your query-plan re-use, as (for strings etc) it uses the length of the value. If you need maximum performance it is better to setup the parameter manually with the defined sizes.
Also note that potentially some of the parameters in the incoming list could be input-output, output or result. I would be very tempted to substitute for something more like:
SqlParameter newParam = cmdExecuteReader.Parameters.Add(
param.ParameterName, param.SqlDbType, param.Size);
newParam.Value = param.Value ?? DBNull.Value;
newParam.Direction = param.Direction;
I did the stuff that you are trying to do, here some examples:
public int ChangeState(int id, int stateId)
{
return DbUtil.ExecuteNonQuerySp("changeDossierState", Cs, new { id, stateId });
}
public IEnumerable<Dossier> GetBy(int measuresetId, int measureId, DateTime month)
{
return DbUtil.ExecuteReaderSp<Dossier>("getDossiers", Cs, new { measuresetId, measureId, month });
}
I recommend you to look here
and to download the samples solution (where there is a DAL Sample project included)
http://valueinjecter.codeplex.com/

Categories

Resources