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
Related
I've got code for inserting a record into a SQL Server table which in some cases has some null values (if not replaced with pseudo-null (empty) vals first). Specifically, they are varchars.
If I don't check for null and assign string.empty in those instances:
if (null == _PatternOrdinal)
{
rs.PatternOrdinal = string.Empty;
}
else
{
rs.PatternOrdinal = _PatternOrdinal;
}
...it throws the exception, "The parameterized query '(#Unit varchar(25),#ReportID int,#NextExecution datetime,#NextEx' expects the parameter '#PatternOrdinal', which was not supplied."
IOW, if the code is simply this:
rs.PatternOrdinal = _PatternOrdinal;
...instead of the if block above, it crashes.
So I have to do that with several parameters. Is there a less verbose way to circumvent this periodic derailing?
You can use null-coalescing operator:
rs.PatternOrdinal = _PatternOrdinal ?? string.Empty;
It returns the left-hand operand if the operand is not null; otherwise it returns the right hand operand.
But be aware that an empty string and a null value are not the same. An empty string is still a value, but a null means that there is no defined value.
In those cases where you need to save null values you should use DbNull.Value instead of an empty string.
I have CustomerID declared as
int? CustomerID=null;
I am checking null values while reading DataReader
Id = reader["CustomerId"] is DBNull ? null :Convert.ToInt32(reader["CustomerID"]);
It is throwing
Type of conditional expression cannot be determined because there
is no implicit conversion between '<null>' and 'int'
What is the problem with the Conversion?
I think you need to do it this way:
if(! reader.IsDBNull(reader.GetOrdinal("CustomerId"))
{
Id = Convert.ToInt32(reader["CustomerID"]);
}
else
{
Id = NULL;
}
You need to use the .IsDBNull method on the reader to determine ahead of time if a column is NULL - if it is, don't even read the value from the reader.
Change your conditon to
reader["CustomerId"] == DBNull.Value
A ?: conditional expression cannot evaluate to two different types on the true and false condition. I think a cast (int?)null should work.
The problem (assuming that Id is declared properly) is that the conditional statement infers the result type from the true result. In your case, that type is null. It then will try to cast the second type to the same as the first...and there is no cast from int to null.
The solution is to cast the true expression to the desired type:
Id = reader["CustomerId"] == DBNull.Value ?
(int?) null :
Convert.ToInt32(reader["CustomerID"]);
Try this
Id = reader["CustomerId"] is DBNull ? (int?)null : Convert.ToInt32(reader["CustomerID"]);
the types of both parts of the ?: need to be explicit
What's happening is your code is incorrectly evaluating and trying to do the Convert function.
Its readding reader["CustomerId"] is DBNull which it isn't its really a DBNull.Value so then it attempts to do Convert.ToInt32("<null>") and crashes. You need to fix your if statement, and add a (int ?) cast.
It should be:
Id = reader["CustomerId"] is DBNull.Value ? (int?) null :Convert.ToInt32(reader["CustomerID"]);
I had ddl which determine gender and user can donot choose any value from ddl so i tried to check if user didnot select any value from ddl inser null value or any value in database i made that but error apear(Procedure or Function 'InsertRegisteration' expects parameter '#Gender_Id', which was not supplied).any one help me
(My Code)
if (DDLGender.SelectedItem.Value[0]!= null )
{
command.Parameters.Add("#Gender_Id",SqlDbType.Int).Value=null;
}
else
{
command.Parameters.Add(Parameter.NewInt("#Gender_Id", DDLGender.SelectedValue));
}
Try this:
if (DDLGender.SelectedItem.Value[0]!= null )
{
command.Parameters.Add("#Gender_Id",SqlDbType.Int).Value= DBNull.Value;
}
else
{
command.Parameters.Add(Parameter.NewInt("#Gender_Id", DDLGender.SelectedValue));
}
Added : 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.
Try this:
if (DDLGender.SelectedIndex == 0 )
{
command.Parameters.Add("#Gender_Id", DBNull.Value);
}
else
{
command.Parameters.Add("#Gender_Id", (int)DDLGender.SelectedValue);
}
You don't have to specify the data type for parameters.
Assuming you are using stored procedure for updating,
I think you can make the change in stored procedure to have default value to this INT column as NULL which will make this parameter optional. Then you can execute only the ELSE part here from code.
You can combine the above lines of code to :
cmd.Parameters.Add(new SqlParameter { SqlDbType = SqlDbType.Int, Value = DDLGender.SelectedIndex!=0 ? (int)DDLGender.SelectedValue : null });
The use of Null coalescing operator will do the null check. If the selectedItem is not null then that will be passed in as value otherwise null will be passed.
I have an Oracle data table fetching columns that be null. So I figure to keep the code nice and simple that I'd use the ?? operand. AlternatePhoneNumber is a string in my C# model.
AlternatePhoneNumber = customer.AlternatePhoneNumber ?? ""
However, even with that code I still get the error.
System.InvalidCastException: Unable to cast object of type 'System.DBNull' to type 'System.String'.
I know what the error means but why is ?? not usable on DBNull? Isn't null and DBNull essentially the same?
Thank you.
The ?? operator only applies to actual nulls.
null and DBNull.Value are not the same; DBNull.Value is simply a placeholder object.
Also, that exception is coming from inside the AlternatePhoneNumber property, before your ?? operator executes. (Your code doesn't have a cast).
If customer is a row in a typed dataset, change the column's NullValue property in the designer.
null and DBNull are not the same. System.DBNull is an actual object.
The problem is that AlternatePhoneNumber is a string. DBNull is not.
Try this instead:
AlternatePhoneNumber = (customer.AlternatePhoneNumber as string) ?? ""
Do this:
public T IfNull<T>(object o, T value)
{
return (o == DbNull.Value) ? value : (T)o;
}
DBNull is a type with a single value, and is not the same as a null string reference, which is why you can't use ??. You could do this however:
string alternativePhoneNumber = DBNull.Value.Equals(customer) ? string.Empty : ((Customer)customer).AlternatePhoneNumber;
As other replies state, null means a reference that refers to no object, while DBNull is a class supplied by ADO.NET to indicate when a field or value is NULL at the database (or in a DataTable).
While you can use the conditional (ternary) operator (?:) to do what you want:
AlternatePhoneNumber = customer.AlternatePhoneNumber is DBNull
? ""
: customer.AlternatePhoneNumber;
I tend to wrap this up in an extension method:
static class NullExtensions
{
public static T WhenNull<T>( this object value, T whenNullValue )
{
return (value == null || value is DBNull)
? whenNullValue
: (T)value;
}
}
which I find makes the code easier to read and understand.
AlternatePhoneNumber = customer.AlternatePhoneNumber.WhenNull( "" );
DBNull is NOT a real "null".
The "??" - operator detects only null - references, not objects that emulate "null" behavior.
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;