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.
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);
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.
I am a developing an application with different text boxs that I am passing to oracle stored procedure using ASP.NET ( c# ). For each textbox there is a property in a class file. When I pass empty textbox it is throwing an error. This is because that a varchar2 field in oracle is not accepting "null".
Private string _fristName;
public string FirstName
{
get { return _fristName; }
set { _fristName= value; }
}
Which is best practice for making _fristName as string.empty or "" ?
In your class' initialization, just set your private variable to a default value of String.Empty.
Private string _fristName = String.Empty;
Getting it will return an empty string, not null; setting it will change the value to whatever.
You could also do this:
Private string _fristName;
public string FirstName
{
get { return _fristName ?? String.Empty; }
set { _fristName= value; }
}
Hope this helps!
EDIT: Took suggestion from #Marc in the comments. Changed the comparison to use the ?? operator. Thanks #Marc!
Before setting the value of the parameter in the stored procedure, check for null. This could be put in a common function. Note this is for sql server, but a similiar function could be done for Oracle. Also, if the column can't take a null, then instead of DBNull.Value, maybe a default is used (String.Empty for strings, etc.).
IDbDataParameter parameter = new SqlParameter(ParameterName, DatabaseFieldType);
parameter.Value = (value== null ? DBNull.Value : value);
I'm writing a C# routine to call a stored proc. In the parameter list I'm passing in, it is possible that one of the values can legally be null. So I thought I'd use a line like this:
cmd.Parameters.Add(new SqlParameter("#theParam", theParam ?? DBNull.Value));
Unfortunately, this returns the following error:
CS0019: Operator '??' cannot be applied to operands of type 'string' and 'System.DBNull'
Now, this seems clear enough, but I don't understand the rationale behind it. Why would this not work? (And often, when I don't understand why something isn't working, it's not that it can't work...it's that I'm doing it wrong.)
Do I really have to stretch this out into a longer if-then statement?
EDIT: (As an aside, to those suggesting to just use "null" as is, it doesn't work. I originally figured null would auto-translated into DBNull too, but it apparently does not. (Who knew?))
Not like that, no. The types have to match. The same is true for the ternary.
Now, by "match", I don't mean they have to be the same. But they do have to be assignment compatible. Basically: in the same inheritance tree.
One way to get around this is to cast your string to object:
var result = (object)stringVar ?? DBNull.Value;
But I don't like this, because it means you're relying more on the SqlParameter constructor to get your types right. Instead, I like to do it like this:
cmd.Parameters.Add("#theParam", SqlDbTypes.VarChar, 50).Value = theParam;
// ... assign other parameters as well, don't worry about nulls yet
// all parameters assigned: check for any nulls
foreach (var p in cmd.Parameters)
{
if (p.Value == null) p.Value = DBNull.Value;
}
Note also that I explicitly declared the parameter type.
new SqlParameter("#theParam", (object)theParam ?? DBNull.Value)
The ?? operator returns the left-hand operand if it is not null, or else it returns the right operand. But in your case they are different types, so it doesn't work.
The Null Coalesce operator only with with data of the same type. You cannot send NULL to the SqlParamater as this will make Sql Server says that you didn't specify the parameter.
You can use
new SqlParameter("#theParam", (object)theParam ?? (object)DBNull.Value)
Or you could create a function that return DBNull when null is found, like
public static object GetDataValue(object o)
{
if (o == null || String.Empty.Equals(o))
return DBNull.Value;
else
return o;
}
And then call
new SqlParameter("#theParam", GetDataValue(theParam))
The reason you can't use the null coalesce operator is that it has to return one type and you are providing more than one type. theParam is a string. DbNull.Value is a reference to a static instance of type System.DbNull. This is what its implementation looks like;
public static readonly DBNull Value = new DBNull();
//the instantiation is actually in the
//static constructor but that isn't important for this example
So if you were to have a NullCoalesce method, what would its return type be? It can't be both System.String and System.DbNull, it has to be one or the other, or a common parent type.
So that leads to this type of code;
cmd.Parameters.Add(
new SqlParameter("#theParam", (object)theParam ?? (object)DBNull.Value)
);
In your stored proc when you declare the incoming variable, have it set the var equal to null and then do not pass it in from your csharp code, it will then pick up the default value from sql
#theParam as varchar(50) = null
and then in your csharp
if (theParam != null)
cmd.Parameters.Add(new SqlParameter("#theParam", theParam));
This is how I usually pass option and/or defaulted values to my stored procs
I'm pretty sure that just passing a null to the SqlParameter constructor results in it being sent as a DBNull.Value... I may be mistaken, since I use the EnterpriseLibraries for DB access, but I'm quite sure that sending a null is fine there.
cmd.Parameters.Add(new SqlParameter("#theParam", (theParam == null) ? DBNull.Value : theParam));
Use this syntax:
(theParam as object) ?? (DBNull.Value as object)
In this case both parts of operator ?? are of the same type.
Not sure the specific answer to your question, but how about this?
string.IsNullOrEmpty(theParam) ? DBNull.Value : theParam
or if blank is ok
(theParam == null) ? DBNull.Value : theParam
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;