Unexpected behavior between String.IsNullOrEmpty and DBNull.Value - c#

I have the following query:
public static string GetCustomerName(string customerNo)
{
string query = "query to get customer";
var myConn= new MYConnection();
using (SqlConnection con = new SqlConnection(myConn.MYConnectionString))
{
con.Open();
SqlCommand cmd = new SqlCommand(query, con);
cmd.Parameters.Add("#customerNo", SqlDbType.NVarChar).Value = customerNo;
object result = cmd.ExecuteScalar();
return result == DBNull.Value ? String.Empty : (string)result;
}
}
I'm calling the method above like this:
string customerName = GetCustomerName(CustomerID);
if (customerName.Contains(Constants.Company.CompanyName))
{
Additional Logic...
}
However, I'm getting a Object Reference Null error if my method doesn't return a customer name. I would think that the GetCustomer method would return an empty string.
If I change the call to get the CustomerName to below, it works perfectly.
string customerName = GetCustomerName(emailAndSTCodeInfo.CustomerID);
if (String.IsNullOrEmpty(customerName))
{
customerName = "";
}
if (customerName.Contains(Constants.Chase.ACCOUNT_NAME))
{
Additional Logic
}
So, my question is, what would be the proper way of handling this if my GetCustomer method doesn't find a record and returns null. I'm currently using the working code above but it seems like a hack or something.
Any help would be greatly appreciated.

ExecuteScalar returns null if no record is returned.
To guarantee that GetCustomerName never returns null, you could change the last line to
return Convert.ToString(result);
Convert.ToString(object) returns an empty string if the argument is either null or DBNull.Value.

If a query returns no rows, then executing it with ExecuteScalar will return null, not DBNull.Value.
So your GetCustomerName method needs to check for a null return value as well as DBNull.Value.

Related

SQL Parameter as DBNull.Value is not working

I'm loading a select box list with a distinct list of values from our database table on CompanyName.
If the value in the column is NULL, I am adding '-- NULL --'
If the value in the column is empty or white space, I'm adding '-- EMPTY / WHITE SPACE --'
Otherwise, it will be the value in the column.
When I am trying to update the parameter to the property value DBNull.value is not working.
Here is my SQL statement:
SELECT Column1
,Column2
,[CreatedBy]
,RestOfColumns...
FROM [dbo].[CompanyTable]
where CompanyName = #DataVisParam
Here is my code:
public DataTable GetDataVisQueryParameterizedResults(string query, string parameterizedSelectedValue, string connectionString)
{
DataTable dt = new DataTable();
using (SqlConnection conn = new SqlConnection(ConnectionUtilities.GetWebConfigConnectionString(connectionString)))
{
conn.Open();
using (SqlDataAdapter sda = new SqlDataAdapter(query, conn))
{
sda.SelectCommand.Parameters.Clear();
sda.SelectCommand.CommandType = CommandType.Text;
SqlParameter param = sda.SelectCommand.Parameters.Add(SharedUtilities.DataVisParameterPlaceholder(), SqlDbType.NVarChar);
/*if (parameterizedSelectedValue == SharedUtilities.DataVisParamSelectBoxValueIsNull())
{
param.Value = DBNull.Value;
}
else*/
if (parameterizedSelectedValue == SharedUtilities.DataVisParamValueSelectBoxIsEmptyOrWhiteSpace())
{
param.Value = string.Empty;
}
else
{
param.Value = parameterizedSelectedValue;
}
sda.Fill(dt);
}
}
return dt;
}
/// <summary>
/// Data Vis parameterized query placeholder.
/// </summary>
/// <returns>Data vis parameterized placeholder text that will be used in the data vis query</returns>
public static string DataVisParameterPlaceholder()
{
return "#DataVisParam";
}
/// <summary>
/// Data vis parameter value is null.
/// </summary>
/// <returns>Parameter value for null on the select box</returns>
public static string DataVisParamSelectBoxValueIsNull()
{
return "-- NULL --";
}
/// <summary>
/// Data vis parameter value is empty or white space.
/// </summary>
/// <returns>Parameter value for empty or white space on the select box</returns>
public static string DataVisParamValueSelectBoxIsEmptyOrWhiteSpace()
{
return "-- EMPTY / WHITE SPACE --";
}
I've tried DBNull.Value and not even setting it, but always 0 records are return for NULL. If I do the empty or white space, it works and 2 records are returned. Everything else is working fine.
Thoughts?
Main Question: Is there a way to NOT have to change the query from = #DataVisParam to IS NULL ?. I really do not want to have to change the query because then I have to make sure they put the text in perfect. I was hoping I could use the sda.SelectCommand.Parameters.Add() to check for NULL, but that appears it is not possible.
I've tried DBNull.Value in the commented out part of my code where this line is: param.Value = DBNull.Value; is set. It should enter this If statement.
Update: Yes, I know in SQL that you usually check IS Null for thanks in SSMS. Do I need to change my actual query from = #dataVisParam to Is Null? If I need to do that, doesn't that mess up the issue with SQL injection?
Update 2:
Appears I just need to replace the text = #DataVisParam with IS NULL when -- NULL -- is selected in the select box
After discussing with a co-worker, since we are replacing the query to IS NULL from = #DataVisParam and we are NOT using user selection / input, then we can accept the fact that there is not a possibility of a security risk.
The one downside to this, is the user that inputs the query must make sure they put the parameter correctly so that we can match it and replace it with IS NULL
public DataTable GetDataVisQueryParameterizedResults(string query, string parameterizedSelectedValue, string connectionString)
{
DataTable dt = new DataTable();
using (SqlConnection conn = new SqlConnection(ConnectionUtilities.GetWebConfigConnectionString(connectionString)))
{
conn.Open();
// update the query with IS NULL to check on null values
if (parameterizedSelectedValue == SharedUtilities.DataVisParamSelectBoxValueIsNull())
{
// replace the param with IS NULL -> replace for one space or two spaces (if there is a typo mistake with updating the query etc.)
#pragma warning disable CA1307 // Specify StringComparison
query = query.Replace("= #DataVisParam", "IS NULL").Replace("= #DataVisParam", "IS NULL");
#pragma warning restore CA1307 // Specify StringComparison
}
using (SqlDataAdapter sda = new SqlDataAdapter(query, conn))
{
// the parameterizedSelectedValue is not Null, query by the parameter and query will not be changed.
if (parameterizedSelectedValue != SharedUtilities.DataVisParamSelectBoxValueIsNull())
{
sda.SelectCommand.Parameters.Clear();
sda.SelectCommand.CommandType = CommandType.Text;
SqlParameter param = sda.SelectCommand.Parameters.Add(SharedUtilities.DataVisParameterPlaceholder(), SqlDbType.NVarChar);
if (parameterizedSelectedValue == SharedUtilities.DataVisParamValueSelectBoxIsEmptyOrWhiteSpace())
{
param.Value = string.Empty;
}
else
{
param.Value = parameterizedSelectedValue;
}
}
sda.Fill(dt);
}
}
return dt;
}

ExecuteScalar() always return NULL

I am trying to return an integer from the database using ExecuteScalar(). However, when I run the query on the database itself, I get the correct answer, and c# gives me a 0 (Null) all the time. I know it returns a null because before i added id = Convert.ToInt32(command.ExecuteScalar()); it would give me an error telling me to make sure NULL is handled. I am expecting it to return a 3 btw.
private int getFamilyId()
{
int id = 0;
using (SqlConnection connection = new SqlConnection(Globaldata.ConnectionString))
{
using (SqlCommand command = new SqlCommand())
{
string sqlString = #"SELECT [Id] FROM [dbo].[FamilyDetails];";
command.Connection = connection;
command.CommandText = sqlString;
try
{
connection.Open();
id = Convert.ToInt32(command.ExecuteScalar());
}
catch (Exception e)
{
MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK);
}
finally
{
connection.Close();
}
return id;
}
}
}
When you do this:
string sqlString = #"SELECT [Id] FROM [dbo].[FamilyDetails];";
You don't want to do this:
id = Convert.ToInt32(command.ExecuteScalar());
Three things could go wrong here.
Problem #1:
If there are no rows in the table, command.ExecuteScalar() wil return Null.
Problem #2:
If there are any rows in the table, command.ExecuteScalar() wil return the value of the first rows it happens to encounter because the SELECT statement is not restricted to 1 value.
Problem #3:
If the Id column is not declared as NOT NULL command.ExecuteScalar() could return a DbNull, which which makes no sense when converted to an Integer.
Try and see what happens when there are 0 or 1 or 2 records in the table.
--UPDATE--
It works now, My connection string had one character missing. I think it happened when I took out the connection Timeout part of the connection string.
Thank you for your suggestions!!!

Why this query returning -1?

I'm trying to get the max id of the table category using this code
string maxid = "";
string query = "SELECT MAX(Cat_ID) + 1 FROM Category";
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["JokerCafe"].ConnectionString);
try
{
conn.Open();
SqlCommand cmd = new SqlCommand(query, conn);
maxid = cmd.ExecuteNonQuery().ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
conn.Close();
}
return maxid;
I run this query in sql it is returning exact value but when try to execute it from code it returns -1. Kindly guide me what's going wrong with it?
ExecuteNonQuery() will return the affected row count. For example if you are trying to execute any update statement or delete statement through ExecuteNonQuery() method then it will return the number of affected rows.
But, if you want to fetch a value from specific field then you need to try ExecuteScalar() method. It will return Object type value. Using this method you can fetch only a single value record.
object val = command.ExecuteScalar();
if (val != null)
{
//Do your stuff here.
}
ExecuteScaler is your solution
It executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored.
so do modify your code to
maxid = cmd.ExecuteScalar().ToString();
or
maxid = cmd.ExecuteScalar() as string; //to be safe side if return value is null
and you'll get the value expected from the query

Would a NULL value in the DB return a null value in the dataset?

Does a NULL value for a (string) column in the database would return a null value in the dataset record in C# (by default)? Or would it turn into an empty string in the dataset?
In .NET depending on what technologies you use, a NULL value will be returned as null or as DBNull.Value.
When using ADO.NET (System.Data), a database NULL value will generally be returned as DbNull.Value, whereas in (for example) Entity Framework, it will be returned as null.
If you are not doing any other processing, the value will not be returned as an empty string and in no case as the string value "null".
Normally it will return the value DBNull.Value or throw a InvalidCastException. Depending on what you are going to it could turn the DBNull.Value in to a null, but usually when stuff like that happens it really is doing the DBNull.Value check for you and just hiding it. It would not be hard to make a extension method of your own that did it too.
public static string GetStringWithNullCheck(this IDataReader reader, int index)
{
if(reader.IsDBNull(index))
return null;
return reader.GetString(index); //If we called this on a record that is null we get a InvalidCastException.
}
This distinction between null and DBNull.Value is most useful when calling ExecuteScalar(), this allows you to tell the diffrence between "no records returned" and "record returned but the DB contained NULL as the value"
using(var cmd = new SqlCommand(query, connection))
{
var result = cmd.ExecuteScalar();
if(result == null)
{
//0 rows where returned from the query
}
else if(result == DBNull.Value)
{
//Rows where returned but the value in the first column in the first row was NULL
}
else
{
//Result is the value of whatever object was in the first column in the first row
}
}

bool doesExist = command.ExecuteScalar() != null for some reason evaluates to true constantly [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
ExecuteScalar returns null or DBNull (development or production server)
I have a stored procedure that checks to see if a pre-existing file id is associated to an item. If the select statement returns values, it should be true and assign "true" to the bool. however when the select statement returns a null because it doesn't exist, my code behind still makes the .Execute return "true"
This is my stored proc:
ALTER PROCEDURE [dbo].[Events_TaskIDExists]
#EventID int
AS
BEGIN
select TaskID from Events where EventID = #EventID
END
and here is my code behind:
public void hasTaskAssociatedToNote()
{
String[] Notes = hidSelectedEventIDs.Value.Split(',');
bool exists = false;
foreach (var note in Notes)
{
var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["OSCIDConnectionString"].ToString());
var command = new SqlCommand("Events_TaskIDExists", connection);
command.Parameters.Add(new SqlParameter("#EventID", SqlDbType.Int));
command.Parameters["#EventID"].Value = Convert.ToInt32(note.Trim());
command.CommandType = CommandType.StoredProcedure;
try
{
connection.Open();
exists = command.ExecuteScalar() != null;//causes true when it returns null......
var temp = command.ExecuteScalar();//this was just to check something else
if (exists)
{
exhibitWarning.Visible = true;
Warning1.Text = "There is an existing Task associated 0.";
}
}
catch (SqlException sql)
{
lblStatus.Text = "Couldn't connect to the Database - Error";
lblStatus.ForeColor = System.Drawing.Color.Red;
}
catch (Exception ex)
{
lblStatus.Text = "An error occured";
lblStatus.ForeColor = System.Drawing.Color.Red;
}
finally
{
if (connection.State == ConnectionState.Open)
connection.Close();
}
}
}
Your exists variable should be set as:
object result = command.ExecuteScalar();
exists = result != DBNull.Value && result != null;
A null result from SqlCommand.ExecuteScalar() returns a DBNull.Value, not null. Only an empty result set will return a null.
Since you're selecting TaskID based on EventID, my guess is that you don't have your database constrained to require a TaskID for every Event, and thus you have null TaskID fields. In other words, you have the Event record containing #EventID, but not an associated Task record (based on TaskID). This condition will return a DBNull.Value rather than a null.
"Return Value Type: System.Object The first column of the first
row in the result set, or a null reference (Nothing in Visual Basic)
if the result set is empty. Returns a maximum of 2033 characters." -
MSDN -
SqlCommand.ExecuteScalar()
I'd probably shotgun this and just
exists = (command.ExecuteScalar() ?? DBNull.Value) != DBNull.Value;
this assumes that for some reason your stored procedure is actually returning a row that has it's first column equal to DBNull, and that in that case you want exists == false. A short debug should prove or disprove that.

Categories