This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Best way to check if a Data Table has a null value in it
I want to know what should be the way to check DBNull for a DataTable - DataRow values.
Ex
I have a DataRow which fetches information from database from rows type like :
varchar, money, Int and so on.
What should be my (simple and sweet) approach to handle such situation.
Try:
foreach(DataRow row in table.Rows)
{
object value = row["ColumnName"];
if (value == DBNull.Value)
{
}
else
{
}
}
Try this
For varchar
string val = dr["name"].ToString();
For int
int? val = dr["status"] == DBNull.Value ? (int?) null : Convert.ToInt32(dr["status"]);
Do the same for Money, Decimal as done for int replacing with respective .Net types
You can use an extension method like this;
public static T GetValue<T>(this OracleDataReader reader, string fieldName)
{
T result = default(T);
int index = reader.GetOrdinal(fieldName);
if (reader.IsDBNull(index))
{
return default(T);
}
if (typeof(T) == typeof(string))
{
result = (T)Convert.ChangeType(reader.GetString(index), typeof(T));
}
if (typeof(T) == typeof(int))
{
result = (T)Convert.ChangeType(reader.GetInt32(index), typeof(T));
}
if (typeof(T) == typeof(DateTime))
{
result = (T)Convert.ChangeType(reader.GetDateTime(index), typeof(T));
}
if (typeof(T) == typeof(byte[]))
{
OracleLob blob = reader.GetOracleLob(index);
result = (T)Convert.ChangeType(blob.Value, typeof(T));
}
return result;
}
And you can use like string title = reader.GetValue<string>("title")
There are clearly-defined mappings for CLR and SQL types, so the question is really how to efficiently and accurately map those types. Long-term, the easiest way is probably to use an automated mapping process which maps the properties of your class to the columns in the DataRow. You can write your own or find many examples/products online (any ORM features this as core functionality).
Assuming that you still want to make manual assignments, you need to determine how you want to handle null values from the database. Do you want to assign them to a corresponding nullable type? do you want to use default(T)? do you want to use another value (default can be a poor substitute for null)? For example, a temperature of 0 degrees is perfectly valid, but default(float) == 0. If you use default(T), you might not be able to tell the difference between zero and a value that was null in the database.
Once you have your assignment strategy defined, put the code into a reusable form (extension methods, helper class, etc.) Prefer unboxing to the exact type when possible, as this will be the fastest. Following that, unbox to type, then cast. Following that, use the Convert class.
Related
I have a table with a few fields, one of them is a Double type field which can contains null values...
Using ADO and SQLDATAReader I recover that field in a variable. I defined this variable as a: Double, Double?, double, double?... and I got the value (coming from de SQLDataReader) using GetValue (and doing a cast) or using a GetDouble... each one is crashing when the value is null.
The only this is working is defining this variable as a object, but I dont want it. Thinking in advance could be hard times handle this type in my project...
Quote: I have to differentiate the case when this value is 0 or null...
Any idea guys?
Edited:
Object.DoubleValue= (Double?)Datos.GetDouble(1);
Object.doubleValue= (double?)Datos.GetDouble(1);
Not working.
Object.ObjectValue= Datos.GetValue(1);
Working.
Unfortunately there's no out of the box method. But you could tweak it with an extension method like this:
(be aware its just a rough prototype that works in your case, but probably needs some checks and constraints etc)
public static class Helpers
{
public static T GetSmartValue<T>(this SqlDataReader r, int ordinal)
{
dynamic value = r.GetValue(ordinal);
if (value == DBNull.Value)
{
value = null;
return value;
}
return (T) value;
}
}
then in your code
var x = yourReader.GetSmartValue<double?>(1);
I have a stored procedure which has a parameter that is nullable to a decimal column. The method I wrote expects a Dictionary<string, string> to pass the parameter name and the value.
However, when I use reflection to GetValue when I do:
collection.Add(model.Name, DBNull.Value.ToString());
In essence all DBNull.Value returns would be a String.Empty. So how can I ensure the null is correctly passed to SQL Server? Otherwise that will throw an exception, because invalid column data type. The documentation states, with ToString it will return String.Empty.
DBNull is a singleton class, which means only this instance of this
class can exist. If a database field has missing data, you can use the
DBNull.Value property to explicitly assign a DBNull object value to
the field. However, most data providers do this automatically. To
evaluate database fields to determine whether their values are DBNull,
you can pass the field value to the DBNull.Value.Equals method.
However, this method is rarely used because there are a number of
other ways to evaluate a database field for missing data. These
include the Visual Basic IsDBNull function, the Convert.IsDBNull
method, the DataTableReader.IsDBNull method, the IDataRecord.IsDBNull
method, and several other methods.
This would be added to the database in the following manner:
command.Parameters.AddWithValue(parameter.Key, parameter.Value);
A traditional Ado.Net SqlDataReader.
I think you will have to get each value from the Dictionary and then use code like that shown below before passing the parameter to the command. In other words, store null as the value in the Dictionary, not DBNull, then call the code below to convert from null to DBNull.Value.
SqlParameter firstNameParam = new SqlParameter("FirstName", System.Data.SqlDbType.NVarChar);
string firstNameValue = null; // this is the value from the Dictionary
if (firstNameValue == null) {
firstNameParam.Value = DBNull.Value;
}
else {
firstNameParam.Value = firstNameValue;
}
After refactoring, you could do something like this:
public static object ToDBNull(object value) {
if (value == null) {
return DBNull.Value;
}
else {
return value;
}
}
Then call ToDBNull function like this:
cmd.Parameters.AddWithValue("FirstName", ToDBNull(firstNameValue));
If you have Dictionary<string, string>, and you have a value in the dictionary such that:
var dictionary = new Dictionary<string, string>() { { "hello", null } };
string data = dictionary["hello"];
var dec = data == null ? (decimal?)null : Convert.ToDecimal(data);
You could write a little extension method to make things easier for passing it to a command
public static object ValueOrDbNull(this decimal? value)
{
if (value.HasValue)
{
return value;
}
return DBNull.Value;
}
And then add it to your command as such:
command.Parameters.Add("#your_proc_param", dec.ValueOrDbNull());
There shouldn't be any issues passing it to a procedure in this manner, since you said it's declared as a nullable decimal field.
I'm working with an existing object framework that uses special values to represent NULL for primitives int, DateTime, long. Data containing these values are added to a DataTable and displayed in third-party controls such as XtraGrid.
All is fine, except when aggregates are applied to the data. In this case, obviously the special values are processed instead of the NULL entries.
So I think the best solution is to map the values to/from DBNull when putting into the DataRow. I thought about subclassing the DataTable and DataRow, but the base classes don't allow overriding of the accessors.
I could add extra Get/Set functions to the subclasses but this relies on remembering to use them. I could add static helper functions instead of subclassing, but this has the same problem.
Is there a more elegant solution?
Update
It's the grid itself that is doing the aggregation, as it has flexible controls to let the user define summaries at run-time. So I think the only real solution is to map to/from DBNull somehow, just looking for an elegant way of doing this.
You may create an extension method to help you fill the datatable and convert values to dbnull:
public static class DataExtensions
{
public static DataRow AddRow(this DataRowCollection rowCollection, params object[] values)
{
object[] newValues = new object[values.Length];
for(int i=0;i<values.Length;i++)
{
object value = values[i];
if (value != null)
{
Type t = value.GetType();
//check for min value only for value types...
if (t.IsValueType)
{
//maybe you can do some caching for that...
FieldInfo info = t.GetField("MinValue",
System.Reflection.BindingFlags.Static
| System.Reflection.BindingFlags.Public
);
if (info != null)
{
object o = info.GetValue(null);
if (value.Equals(o)) //very important == will return false
{
value = DBNull.Value;
}
}
}
}
newValues[i] = value;
}
return rowCollection.Add(newValues);
}
}
And then you will be able to write something like:
t.Rows.AddRow(a,b,c,d,e);
maybe you can create conditional aggregates with IIF like (silly example):
DataTable t= new DataTable();
t.Columns.Add("id");
t.Columns.Add("id2");
t.Columns.Add("id3", typeof(int), "IIF(id="+int.MinValue.ToString()+",id2,id+id2)");
for (int i = 0; i < 5; i++)
{
t.Rows.Add(new object[] { i, 2 * i });
}
t.Rows.Add(new object[] { int.MinValue, 2000});
Edit: adapted to your comment on other post.
I have a DataTable, which has a number of columns. Some of those columns are nullable.
DataTable dt; // Value set.
DataRow dr; // Value set.
// dr["A"] is populated from T-SQL column defined as: int NULL
What, then, is the cleanest form of converting from a value in a DataRow, to a nullable variable.
Ideally, I would be able to do something like:
int? a = dr["A"] as int?;
Edit: Turns out you CAN do this, the side effect being that if your Schema types arn't ints, then this is ALWAYS going to return null. The answer by Ruben of using dr.Field<int?>("A") ensures type mismatches don't silently fail. This, of course, will be picked up by thorough unit tests.
Instead I'm usually typing something along the lines of:
int? a = dr["A"] != DBNull.Value ? (int)dr["A"] : 0;
This is a bunch more keystrokes, but more importantly, there's more room for someone to stuff something up with a wrong keystroke.
Yes, a Unit Test will pick this up, but I'd rather stop it altogether.
What is the cleanest, least error-prone pattern for this situation.
The LINQ to DataSets chapter of LINQ in Action is a good read.
One thing you'll see is the Field<T> extension method, which is used as follows:-
int? x = dr.Field<int?>( "Field" );
Or
int y = dr.Field<int?>( "Field" ) ?? 0;
Or
var z = dr.Field<int?>( "Field" );
This is the purpose of the DataRowExtensions class in .NET 3.5, which provides static Field<T> and SetField<T> methods for round-tripping nullable (and non-nullable) data between the DataRow and .NET types.
int? fld = row.Field<int?>("ColumnA")
will set fld to null if row["ColumnA"] contains DBNull.Value, to its value if it contains an integer, and throw an exception if it contains anything else. And on the way back,
row.SetField("ColumnA", fld);
does the same thing in reverse: if fld contains null, it sets row["ColumnA"] to DBNull.Value, and otherwise sets it to the value of fld.
There are overloads of Field and SetField for all of the value types that DataRow supports (including non-nullable types), so you can use the same mechanism for getting and setting fields irrespective their data type.
int? a = (int?)dr["A"]
Why not use LINQ? It does the conversion for you.
Following would work, safely:
Snip:
public static class SqlDataReaderEx
{
public static int TryParse(SqlDataReader drReader, string strColumn, int nDefault)
{
int nOrdinal = drReader.GetOrdinal(strColumn);
if (!drReader.IsDbNull(nOrdinal))
return drReader.GetInt32(nOrdinal);
else
return nDefault;
}
}
Usage:
SqlDataReaderEx.TryParse(drReader, "MyColumnName", -1);
Extension methods!
Something like the following:
public static class DataRowExtensions
{
public static Nullable<T> GetNullableValue<T>(this DataRow row, string columnName)
where T : struct
{
object value = row[columnName];
if (Convert.IsDBNull(value))
return null;
return (Nullable<T>)value;
}
public static T GetValue<T>(this DataRow row, string columnName)
where T : class
{
object value = row[columnName];
if (Convert.IsDBNull(value))
return null;
return (T)value;
}
}
Use it like so:
int? a = dr.GetNullableValue<int>("A");
or
string b = dr.GetValue<string>("B");
public static object GetColumnValue(this DataRow row, string columnName)
{
if (row.Table.Columns.Contains(columnName))
{
if (row[columnName] == DBNull.Value)
{
if (row.Table.Columns[columnName].DataType.IsValueType)
{
return Activator.CreateInstance(row.Table.Columns[columnName].DataType);
}
else
{
return null;
}
}
else
{
return row[columnName];
}
}
return null;
}
To call the function you could write
var dt = new DataTable();
dt.Columns.Add("ColumnName");
....
Add rows in Datatable.
....
dt.Rows[0].GetColumnValue("ColumnName);
Chart.data = new List < int ?> ();
Chart.data = (from DataRow DR in _dtChartData.Rows
select(int ? )((DR[_ColumnName] == DBNull.Value) ? (int ? ) null : (int ? ) DR[_ColumnName])).ToList();
When looping through a DataRow and encountering types such as
DataRow dr;
dr["someString"]
dr["someInteger"]
dr["somedata"]
What's the best way to get them into their corresponding data types? dr["foo"] is just a generic object.
Also, are these able to be easily converted to nullable types? dr["someInteger"] could be null.
When reading from a DataRow, your biggest enemy is a null value. In a DataRow, when a value is null, it is not equals to null: It is equals to DBNull.Value.
if(DBNull.Value == null)
{
// Will never happen
}
Unless you know that your field cannot be null, it is not safe to cast. For example, the following example will fail if the data is DBNull:
string name = (string)dr["Name"];
If you can use the LINQ extensions, you can include the reference System.Data.DataSetExtensions and the namespace System.Data and call
string name = dr.Field<string>("Name");
If you cannot use LINQ, then you have to fall back to checking for null value with
string name = null;
if(!dr.IsNull("Name"))
name = (string)dr["Name"];
Or you could code your own Field function like this:
public static T GetValue<T>(object value)
{
if (value == null || value == DBNull.Value)
return default(T);
else
return (T)value;
}
and get your value this way:
string name = GetValue<string>(dr["Name"]);
If you can use .net 3.5, then you can use the Field extension method to more easily access the data if you know the type. An example would be:
string somestring= row.Field<string>("SomeString");
Otherwise you're stuck with casting the field to the type of the object the old fashioned way.
Simply casting the values to the right type should work:
(string) dr["someString"];
(int?) dr["someInteger"];
(byte[]) dr["somedata"];
string GetString(DataRow dr, string ColumnName)
{
if (dr.IsNull(ColumnName))
{
return null;
}
return (string)dr[ColumnName];
}
Another option is to use "as"
string str = dr["someString"] as string;
if it's DBNull.Value (or any other object not of type string), then str will get a real "null". Otherwise it will get the proper string value.
For value types, you can use nullable, i.e.
int? i = dr["someint"] as int?;
Again, it will get a real "null" instead of DBNull.Value. However, with nullable types you have to remember to use .Value, i.e.
int x = i.Value + 5;