I frequently have problems dealing with DataRows returned from SqlDataAdapters. When I try to fill in an object using code like this:
DataRow row = ds.Tables[0].Rows[0];
string value = (string)row;
What is the best way to deal with DBNull's in this type of situation.
Nullable types are good, but only for types that are not nullable to begin with.
To make a type "nullable" append a question mark to the type, for example:
int? value = 5;
I would also recommend using the "as" keyword instead of casting. You can only use the "as" keyword on nullable types, so make sure you're casting things that are already nullable (like strings) or you use nullable types as mentioned above. The reasoning for this is
If a type is nullable, the "as" keyword returns null if a value is DBNull.
It's ever-so-slightly faster than casting though only in certain cases. This on its own is never a good enough reason to use as, but coupled with the reason above it's useful.
I'd recommend doing something like this
DataRow row = ds.Tables[0].Rows[0];
string value = row as string;
In the case above, if row comes back as DBNull, then value will become null instead of throwing an exception. Be aware that if your DB query changes the columns/types being returned, using as will cause your code to silently fail and make values simple null instead of throwing the appropriate exception when incorrect data is returned so it is recommended that you have tests in place to validate your queries in other ways to ensure data integrity as your codebase evolves.
If you aren't using nullable types, the best thing to do is check to see if the column's value is DBNull. If it is DBNull, then set your reference to what you use for null/empty for the corresponding datatype.
DataRow row = ds.Tables[0].Rows[0];
string value;
if (row["fooColumn"] == DBNull.Value)
{
value = string.Empty;
}
else
{
value = Convert.ToString(row["fooColumn"]);
}
As Manu said, you can create a convert class with an overloaded convert method per type so you don't have to pepper your code with if/else blocks.
I will however stress that nullable types is the better route to go if you can use them. The reasoning is that with non-nullable types, you are going to have to resort to "magic numbers" to represent null. For example, if you are mapping a column to an int variable, how are you going to represent DBNull? Often you can't use 0 because 0 has a valid meaning in most programs. Often I see people map DBNull to int.MinValue, but that could potentially be problematic too. My best advice is this:
For columns that can be null in the database, use nullable types.
For columns that cannot be null in the database, use regular types.
Nullable types were made to solve this problem. That being said, if you are on an older version of the framework or work for someone who doesn't grok nullable types, the code example will do the trick.
Add a reference to System.Data.DataSetExtensions, that adds Linq support for querying data tables.
This would be something like:
string value = (
from row in ds.Tables[0].Rows
select row.Field<string>(0) ).FirstOrDefault();
I always found it clear, concise, and problem free using a version of the If/Else check, only with the ternary operator. Keeps everything on one row, including assigning a default value if the column is null.
So, assuming a nullable Int32 column named "MyCol", where we want to return -99 if the column is null, but return the integer value if the column is not null:
return row["MyCol"] == DBNull.Value ? -99 : Convert.ToInt32(Row["MyCol"]);
It is the same method as the If/Else winner above - But I've found if you're reading multiple columns in from a datareader, it's a real bonus having all the column-read lines one under another, lined up, as it's easier to spot errors:
Object.ID = DataReader["ID"] == DBNull.Value ? -99 : Convert.ToInt32(DataReader["ID"]);
Object.Name = DataReader["Name"] == DBNull.Value ? "None" : Convert.ToString(DataReader["Name"]);
Object.Price = DataReader["Price"] == DBNull.Value ? 0.0 : Convert.ToFloat(DataReader["Price"]);
If you have control of the query that is returning the results, you can use ISNULL() to return non-null values like this:
SELECT
ISNULL(name,'') AS name
,ISNULL(age, 0) AS age
FROM
names
If your situation can tolerate these magic values to substitute for NULL, taking this approach can fix the issue through your entire app without cluttering your code.
DBNull implements .ToString() like everything else. No need to do anything. Instead of the hard cast, call the object's .ToString() method.
DataRow row = ds.Tables[0].Rows[0];
string value;
if (row["fooColumn"] == DBNull.Value)
{
value = string.Empty;
}
else
{
value = Convert.ToString(row["fooColumn"]);
}
this becomes:
DataRow row = ds.Tables[0].Rows[0];
string value = row.ToString()
DBNull.ToString() returns string.Empty
I would imagine this is the best practice you're looking for
It is worth mentioning, that DBNull.Value.ToString() equals String.Empty
You can use this to your advantage:
DataRow row = ds.Tables[0].Rows[0];
string value = row["name"].ToString();
However, that only works for Strings, for everything else I would use the linq way or a extension method. For myself, I have written a little extension method that checks for DBNull and even does the casting via Convert.ChangeType(...)
int value = row.GetValueOrDefault<int>("count");
int value = row.GetValueOrDefault<int>("count", 15);
Often when working with DataTables you have to deal with this cases, where the row field can be either null or DBNull, normally I deal with that like this:
string myValue = (myDataTable.Rows[i]["MyDbNullableField"] as string) ?? string.Empty;
The 'as' operator returns null for invalid cast's, like DBNull to string, and the '??' returns
the term to the right of the expression if the first is null.
You can also test with Convert.IsDBNull (MSDN).
I usually write my own ConvertDBNull class that wraps the built-in Convert class. If the value is DBNull it will return null if its a reference type or the default value if its a value type.
Example:
- ConvertDBNull.ToInt64(object obj) returns Convert.ToInt64(obj) unless obj is DBNull in which case it will return 0.
For some reason I've had problems with doing a check against DBNull.Value, so I've done things slightly different and leveraged a property within the DataRow object:
if (row.IsNull["fooColumn"])
{
value = string.Empty();
}
{
else
{
value = row["fooColumn"].ToString;
}
Brad Abrams posted something related just a couple of days ago
http://blogs.msdn.com/brada/archive/2009/02/09/framework-design-guidelines-system-dbnull.aspx
In Summary "AVOID using System.DBNull. Prefer Nullable instead."
And here is my two cents (of untested code :) )
// Or if (row["fooColumn"] == DBNull.Value)
if (row.IsNull["fooColumn"])
{
// use a null for strings and a Nullable for value types
// if it is a value type and null is invalid throw a
// InvalidOperationException here with some descriptive text.
// or dont check for null at all and let the cast exception below bubble
value = null;
}
else
{
// do a direct cast here. dont use "as", "convert", "parse" or "tostring"
// as all of these will swallow the case where is the incorect type.
// (Unless it is a string in the DB and really do want to convert it)
value = (string)row["fooColumn"];
}
And one question... Any reason you are not using an ORM?
You should also look at the extension methods. Here are some examples to deal with this scenerio.
Recommended read
If you are concerned with getting DBNull when expecting strings, one option is to convert all the DBNull values in the DataTable into empty string.
It is quite simple to do it but it would add some overhead especially if you are dealing with large DataTables. Check this link that shows how to do it if you are interested
Related
I am trying to pull information out of a database and then add the values together. I have no issue when pulling the information out of the database however, there seems to be a conversion error when I am adding the values.
Cannot implicitly convert type 'double?' to 'double'. An explicit conversion exists (are you missing a cast?)
Outside where I am querying I have this:
var addition = 0.0;
When I pull the data out the Database, I try to add the value to this:
addition = addition + clear;
Both addition and clear are the same datatype which is why I am confused why it would need to do any conversion? (both Double's).
Extra information: clear comes from :
var clear = tbl_detail[a].AmountCleared;
and tbl_detail is an array of the queried data.
Could anyone possibly reference why I am receiving this error? Or possibly point me in the right direction because I am not understand why:
It needs to convert anything since they're the same datatype and
What a cast actually is because Google isn't really helping me when I am searching any of this.
It looks like AmountCleared is a nullable double (double?). You could do this:
addition += clear.Value
This will throw a InvalidOperationException if the Clear is null.
[Edit] You could also use addition += clear ?? 0.0 if you would like to use a default value for nulls.
You could change AmoutCleared into a double. I suspect that you created this through a designer or database first and used 'allow nulls' on the database column, which is why your are getting a nullable type. So you would have to change the database type to not 'allow nulls'. But without seeing the definition of the data type tbl_detail[a] or knowing how you generated it, I cannot help you much further.
AmountCleared is actually a Nullable type (Nullable) which allows C# value types to represent SQL server Nulls (Value types can never be assigned the value of null)
You can get the underlying value of the nullable type by simply calling .Value property, however this will throw a InvalidOperationException if the value is null, you can check if it has a value by calling .HasValue
Try this:
addition += clear.HasValue ? clear.Value : 0;
clear is a nullable value
to safely manipulate nullables use:
if(clear.hasValue)
{
addition += clear.Value;
}
You can also use the null coalescing operator ?? to fallback to a default value if the provided value is null
addition += clear ?? 0.0;
Is it possible to test if a variable is defined as a string if the value inside it is null?
If I write:
string b = null;
bool c = b is string;
Then c will be false because is looks at the content, which is null and not a string.
If I write:
string b = null;
bool c = (b.GetType() == typeof(string));
Then it crashes because s is null and you can't call GetType() on a null value.
So, how can I check b to find out what type it is? Some kind of reflection maybe? Or is there any simpler way?
Edit 1: Clarification of the question!
I was a bit unclear in my question and that was my fault. In the example it looks like I'm trying to test the content of the variable. But I want to test the variable itself without looking at the content. In the code examples given I can see that b is a string, but what if I don't know if b is a string and just want to test the variable s to see if it is a string or not.
So, how can I know what type the variable is defined as? As in this example, but x is an unknown variable that might be defined as a string and it might be null as well (since it might be null this example won't work).
bool c = (x.GetType() == typeof(string));
Edit 2: The working solution!
Thanks to all the answers given I was able to solve it. This is how the working solution became. I first created a help function to test the defined type of a variable that works even if the value is null and it doesn't point to anything.
public static Type GetParameterType<T>(T destination)
{
return typeof(T);
}
Then I can just call this function and test my "suspected string" and find out if it really is a string or not.
// We define s as string just for this examples sake but in my "definition" we wouldn't be sure about whether s is a string or not.
string s = null;
// Now we want to test to see if s is a string
Type t = GetParameterType(s);
b = t == typeof(string); // Returns TRUE because s has the type of a string
b = t is string; // Returns FALSE because the content isn't a string
This is just what I wanted to find out!!! Thank you all for squeezing your brains...
You cannot check the type of null because null has no type. It doesn't reference anything at all, therefore there is nothing that C# can look at to find out the actual type.
(Everyone else seems to be answering the question "How can I tell if a string reference is null or empty - but I think the question is "how can I tell if the underlying type of a null reference is string...)
There might be a way to fiddle it though - you might be able to use a generic method as mentioned here:
.NET : How do you get the Type of a null object?
(That link was posted by someone else - not me - as a comment to your original post!)
So you want to know if there is a direct method to check of an object type whose value is set to NULL
In The simple word the answer is NO.
A null reference does not point to any Storage Location, so there is no metadata from which it can make that determination.
Although if you already know it is of type String , you can use following two functions for checking null values
String.IsNullOrWhiteSpace(stringObject);
and
String.IsNullOrEmpty(stringObject)
The best that you could do to set a value to unknown type is use
Convert.ChangeType
e.g. as given in .NET : How do you get the Type of a null object?
public void GetObjectValue<T>(out T destination)
{
object paramVal = "Blah.Blah.";
destination = default(T);
destination = Convert.ChangeType(paramVal, typeof(T).GetType());
}
The type of T can be inferred, so you shouldn't need to give a type parameter to the method explicitly.
Here it is
String.IsNullOrEmpty(s);
If you want to tell whether the actual value is a string, you can't do that with null, as null doesn't have a type.
So it seems you want to determine the actual type of the variable (the one it was declared as):
If the variable is of type string, then you know it at compile time (you declared it as string, after all).
If the variable is generic (like in generic types or a generic method), you can test it via typeof(T), assuming T is your type parameter.
If you got the variable as object though (e.g. as argument of a method), then there is no way to determine its original type if its value is null.
Use the String.IsNullOrEmpty method to check it.
string b = null;
bool c = String.IsNullOrEmpty(b);
See this MSDN link for further details.
IsNullOrWhiteSpace(variableName)
string.IsNullOrEmpty()
var.Trim().Length < 1
The third one is the one I personally use, as it is version-independent.
I'm writing a linq-to-sql query and I'm loading an ID (a bigint in the database and a long in my code) from a table, something like this:
var SomeQuery = (from x in ...
select x.ID).SingleOrDefault();
When I get the result, I use SingleOrDefault in case the return is empty. Does that mean that if the result is empty the SomeQuery variable will be 0 or null?
Thanks.
If you look the documenation for SingleOrDefault
Returns the only element of a sequence, or a default value if the
sequence is empty; this method throws an exception if there is more
than one element in the sequence.
It cleary states that if the sequence is empty it will return the default value which for long and bigint is 0. Why explained below
Documenation for default keyword states
In generic classes and methods, one issue that arises is how to assign
a default value to a parameterized type T when you do not know the
following in advance:
Whether T will be a reference type or a value type.
If T is a value type, whether it will be a numeric value or a struct.
Given a variable t of a parameterized type T, the statement t = null
is only valid if T is a reference type and t = 0 will only work for
numeric value types but not for structs. The solution is to use the
default keyword, which will return null for reference types and zero
for numeric value types.
Default is returned if no element found.(default value of int is 0)
Value is returned if one is found.
Exception is thrown if more than one is found.
Is there any difference between null and System.DBNull.Value? If yes, what is it?
I noticed this behavior now -
while (rdr.Read())
{
if (rdr["Id"] != null) //if (rdr["Id"] != System.DBNull.Value)
{
int x = Convert.ToInt32(rdr["Id"]);
}
}
While I retrieve data from the database using a sql datareader, though there is no value returned if(rdr["Id"] != null) returned true and eventually threw an exception for casting a null as integer.
But, this if I use if (rdr["Id"] != System.DBNull.Value) returns false.
What's the difference between null and System.DBNull.Value?
Well, null is not an instance of any type. Rather, it is an invalid reference.
However, System.DbNull.Value, is a valid reference to an instance of System.DbNull (System.DbNull is a singleton and System.DbNull.Value gives you a reference to the single instance of that class) that represents nonexistent* values in the database.
*We would normally say null, but I don't want to confound the issue.
So, there's a big conceptual difference between the two. The keyword null represents an invalid reference. The class System.DbNull represents a nonexistent value in a database field. In general, we should try avoid using the same thing (in this case null) to represent two very different concepts (in this case an invalid reference versus a nonexistent value in a database field).
Keep in mind, this is why a lot of people advocate using the null object pattern in general, which is exactly what System.DbNull is an example of.
From the documentation of the DBNull class:
Do not confuse the notion of null in an object-oriented programming language with a DBNull object. In an object-oriented programming language, null means the absence of a reference to an object. DBNull represents an uninitialized variant or nonexistent database column.
DBNull.Value is annoying to have to deal with.
I use static methods that check if it's DBNull and then return the value.
SqlDataReader r = ...;
String firstName = getString(r[COL_Firstname]);
private static String getString(Object o) {
if (o == DBNull.Value) return null;
return (String) o;
}
Also, when inserting values into a DataRow, you can't use "null", you have to use DBNull.Value.
Have two representations of "null" is a bad design for no apparent benefit.
DBNull.Value is what the .NET Database providers return to represent a null entry in the database. DBNull.Value is not null and comparissons to null for column values retrieved from a database row will not work, you should always compare to DBNull.Value.
http://msdn.microsoft.com/en-us/library/system.dbnull.value.aspx
DataRow has a method that is called IsNull() that you can use to test the column if it has a null value - regarding to the null as it's seen by the database.
DataRow["col"]==null will allways be false.
use
DataRow r;
if (r.IsNull("col")) ...
instead.
Null is similar to zero pointer in C++. So it is a reference which not pointing to any value.
DBNull.Value is completely different and is a constant which is returned when a field value contains NULL.
This is something I thought would be easier than it's turning out to be. For whatever reason, I can't seem to figure out a way to make what I'm trying to do here work with an If statement:
List<int> miscTimes = new List<int>();
for (int i = 0; i < MISCdataGridView1.RowCount; i++)
{
if (MISCdataGridView1.Rows[i].Cells[2].Value == "Something")
{
miscTimes.Add(Convert.ToInt32(MISCdataGridView1.Rows[i].Cells[3].Value));
}
}
return miscTimes;
For some reason, I can't get it to like anything I do with the if statement:
if (MISCdataGridView1.Rows[i].Cells[2].Value == "Something")
it doesn't throw an exception, but it's not building my list. It has the green underline and says "Possible unintended reference comparison; cast the left side type to 'string'"
I've tried converting to string and all of that. How should I go about this?
The DataGridViewCell.Value property is of type Object and therefore you have to cast to String
(String)dataGridview.Rows[i].Cells[j].Value == "Something"
or rely on Object.Equals().
Object.Equals(dataGridview.Rows[i].Cells[j].Value, "Something")
Using Object.Equals() is more robust because it can deal with the value not being of type String. On the other hand using the cast emphasizes the fact that the value must be a String and will throw an exception if it is not - clearly showing that you probably have a bug.
It looks like "Value" returns an object, and you are comparing against a string, so it is comparing references, when you probably want to compare the value, i.e., "Something". Cast the left side to a string (or call ToString(), whatever, just make sure that you are explicitly comparing two strings).
The Value property is of type Object.
Therefore, you're using the standard == operator, which compares objects by reference.
To compare strings correctly, you need to compare them by value. (Because you can have two different String instances holding the same value)
Change it to
if ((string)MISCdataGridView1.Rows[i].Cells[2].Value == "Something")