Recovering double´s null - c#

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);

Related

Extend the Datarow extension method

I am trying to extend the Field method of the DataRow extension to add a parameter to check if the column exists:
public static T? FieldValue<T>(
this DataRow row,
string columnName,
bool checkColumn = false) where T : struct
{
return
checkColumn
&& !row.Table.Columns.Contains(
columnName)
? default(T)
: row.Field<T>(
columnName);
}
This works fine for int, datetime etc. However, when I try to use it with string it shows error:
The type string must be non-nullable
I am also getting error if there is a null value in the database:
Cannot cast DBNull.Value to type 'System.Decimal'
Is there a way it can be extended the Dataextension seamlessly?
As Markus pointed out, you've got two challenges here. One is about structs vs value types and the other one is to do with the fact that you have to deal with null values.
The second one is trivial to tackle: Just add a ? to your implementation like this: row.Field<T?>(columnName) and your exceptions will be gone.
The first problem, however, is a nasty and frequently encountered one. I am unaware of a pretty way of solving this. Let me still suggest something:
Based on your code above I assume that you are happy to get back Nullable types even for non-nullable columns. So here is something you could do to support reference types on top of what you have and still avoid too much code duplication:
// value type version
public static T? FieldValueStruct<T>(this DataRow row, string columnName, bool checkColumn = false)
where T : struct
{
return row.GetValue(columnName, checkColumn, default(T), row.Field<T? /*with a question mark!!!*/ >);
}
// reference type version
public static T FieldValueClass<T>(this DataRow row, string columnName, bool checkColumn = false)
where T : class
{
return row.GetValue(columnName, checkColumn, default(T), row.Field<T>);
}
// shared amongst value and reference type implementation
private static T GetValue<T>(this DataRow row, string columnName, bool checkColumn, T defaultValue, Func<string, T> getter)
{
return checkColumn && !row.Table.Columns.Contains(columnName)
? defaultValue
: getter(columnName);
}
With this code in place, you get the functionality you want but at a price: You will need to specify type parameters (just like you do now) when you call these methods because type inference won't work (here is why).
string s;
// no type inference, type parameter must be specified
s = row.FieldValueClass<string>("test");
Also, you will need to differentiate in your calls between the value type version and the reference type version which simply isn't pretty. Why do we need to use two different names for the methods? The reason for that is that you cannot overload methods by simply adding different type constraints (see here).
The type inference topic could be solved by using an out parameter which, however, comes again with a bunch of downsides...
// value type version that works with type inference
public static void FieldValueStruct<T>(this DataRow row, string columnName, out T? value, bool checkColumn = false)
where T : struct
{
value = row.GetValue(columnName, checkColumn, default(T), row.Field<T?>);
}
// reference type version that works with type inference
public static void FieldValueClass<T>(this DataRow row, string columnName, out T value, bool checkColumn = false)
where T : class
{
value = row.GetValue(columnName, checkColumn, default(T), row.Field<T>);
}
Now, you can call your method without the type parameter like this:
string s;
// with type inference, doesn't work with properties, though, only with fields
row.FieldValueClass("test", out s);
Unfortunately, this does not work with properties - only with fields.
You see, the world is evil and, sometimes, we cannot do too much about it. ;)
Update based on your comment:
The code below changes your semantics a little but perhaps that's ok:
public static T FieldValue<T>(this DataRow row, string columnName, bool checkColumn = false)
{
return checkColumn && !row.Table.Columns.Contains(columnName)
? default(T)
: row.Field<T>(columnName);
}
Calling this method would need to look like:
// this will return 0 if the column is not available, a null value from the database will cause an exception
int i = r.FieldValue<int>("test");
// this will return null if the column is not available, a null value from the database would be ok
int? iNullable = r.FieldValue<int?>("test");
// this will return null if the column is not available, a null value from the database would be ok
string s = r.FieldValue<string>("test");
The reason for the first error message is the where-constraint:
where T : struct
This constraint requires that each type T that is used as a type parameter is a value type. string is a reference type, hence the error message. In order tomsolve the problem, you should remove the constraint if you don't need it.
As regards the Null-value problem, you should check whether the column is null (if it exists) and in this case also return default(T). You can use the DataRow.IsNull method to check whether the cell is null.

What in the DBNull

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.

Store a null in System.Web.HttpContext.Current.Cache

In an MVC App we are suppose to dynamically load some strings from database. Basically a <string,string> key-value dictionary. Loading each text each time from the database would kill the application, so those texts would normally be pulled from the DB just once and then stored in cache (namely Web.HttpContext.Current.Cache). There are also default texts that are hardcoded and those would be used if nothing was found in the database. To make things more simple I would also put those default texts in the cache.
The logic is as follows:
Try getting the text from cache and return it if it's found.
If there was no text in the cache, try pulling it from the databaseand and if it's found, save it to cache and return it.
If all above failed, use the default text and save it to the cache so no further queries will be made for this particular key.
If I understand it correctly the only way to check if key is set in Web.HttpContext.Current.Cache is by comparing it's value to a null. My problem is, it's not entirely unlikely that the default text will be a null. So if I put that in the cache, I will not know that the key was set and I will try to pull the non-existant value from the database each time the text is needed.
I know I could use an empty string instead of a null, but it's possible that I might need to distinguish between nulls and empty strings for some reason in the near future.
So is there some way to tell if a key is set in the Web.HttpContext.Current.Cache when the value assigned is a null?
StriplingWarrior beat me to the punch, but I agree with what he said: just wrap what you're storing in a complex type so you can do a null check. Normally you'd use Nullable<T> for this, but you can't use a Nullable type for things that are already considered nullable, like strings.
Here's a practical example:
Define a simple wrapper class:
public class CacheEntry<T>
{
public T Value { get; private set; }
public CacheEntry(T value)
{
Value = value;
}
}
And then use it!
System.Web.HttpContext.Current.Cache.Insert("name", new CacheEntry<string>("Marcin"));
var name = (CacheEntry<string>) System.Web.HttpContext.Current.Cache.Get("name");
if (name != null)
{
Console.WriteLine(name.Value);
}
null is highly overused by most C# developers. If you recognize that these values are optional (meaning they may or may not be there), you may want to make your cache entries be of some type that wraps the actual type you're storing. That way, getting a null value means that there is no entry, whereas getting a non-null value that has a null Value property on it means it exists in the cache, but its value is null.
Incidentally, I've been working on a library to represent exactly this sort of wrapper. It's still in its alpha phases at the moment, but at least for this purpose it should be safe to use. You can get it from Nuget under the name "CallMeMaybe".
object result = HttpContext.Current.Cache.Get(key);
if(result != null)
{
return ((Maybe<string>)result).Else(() => null);
}
var value = Maybe.From(GetValueFromDb());
HttpContext.Current.Cache.Add(key, value, ...);
return value;
Another option would be to use MemoryCache directly, which I think is what backs the HttpContext.Current.Cache these days, but which provides additional methods like GetCacheItem.
If you must store a null in something that doesn't distinguish between nulls and key-not-found conditions, store an "active null" object to represent a null you've actually added:
private static readonly object ActiveNull = new object();
public bool GetIfPresent(string key, out object value)
{
object fromCache = Web.HttpContext.Current.Cache.Get(key);
if(fromCache == null)
{
//failed to obtain.
value = null;
return false;
}
if(ReferenceEquals(fromCache, ActiveNull))
{
//obtained value representing null.
value = null;
return true;
}
value = fromCache;
return true;
}
public void AddToCache(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback)
{
Web.HttpContext.Current.Cache.Add(key, value ?? ActiveNull, dependencies, absoluteExpiration, slidingExpiration, priority, onRemoveCallback);
}

Best way to handle Datarow DBNull [duplicate]

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.

What's the recommended way to start using types from a returned DataRow in C#?

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;

Categories