I'm pretty new to C# and am struggling with the following query.
I'm trying to insert values into a database, but it won't accept null as a value. I need to differentiate between 0 and nothing in this field as it shows whether a calculation has occurred or not.
Here's what I have so far:
System.Data.OleDb.OleDbConnection objConn = new System.Data.OleDb.OleDbConnection();
var connStr = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Z:\Projects\database.accdb";
System.Data.OleDb.OleDbCommand ins = new System.Data.OleDb.OleDbCommand();
objConn.ConnectionString = connStr;
objConn.Open();
ins.Connection = objConn;
ins.CommandText = "INSERT INTO AutomatedDecisionProcess(item1, item2, item3) VALUES(#item1, #item2, #item3)";
ins.Parameters.AddWithValue("#item1", SolveBlanks(row[0]));
ins.Parameters.AddWithValue("#item2", SolveBlanks(row[1]));
ins.Parameters.AddWithValue("#item3", SolveBlanks(row[2]));
ins.ExecuteNonQuery();
objConn.Close();
ins.Dispose();
Console.WriteLine("success?");
and I am trying this function I've written:
public static string SolveBlanks(object number)
{
string answer = number.ToString();
if (answer == "")
{
answer = null;
}
return answer;
}
Any ideas?
Answer from : What is the difference between null and System.DBNull.Value?
This may help you...
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.
You should study about DBNull
Let's try the following code:
public static object SolveBlanks(object number)
{
object answer = number.ToString();
if (answer == "")
{
answer = DBNull.Value;
}
return answer;
}
Related
I have a method to show a value in a TextBox:
public double sumaEntregasDos(int id)
{
conectar();
//cmd2.CommandText = "SELECT SUM(pc.entrega) FROM PagosClientes pc INNER JOIN Clientes c ON pc.idCliente = c.idCliente WHERE c.nombre = #nombreCliente";
//cmd2.Parameters.AddWithValue("#nombreCliente", nombre);
cmd2.CommandText = "SELECT SUM(entrega) FROM PagosClientes WHERE idCliente in (" +
id + ")";
double suma = Convert.ToDouble(cmd2.ExecuteScalar());
desconectar();
return suma;
}
When I press the Button, the program shows me the following error:
Object cannot be cast from DBNull to other types.
How can I solve this problem?
The standard way is to check for DBNull:
var result = cmd2.ExecuteScalar();
return result == DBNull.Value ? default : (double)result;
It's a bit cumbersome, so often it's easier to just write SELECT ISNULL(..., 0) ...
Further points:
This only applies if you get an actual NULL result. If there are no rows at all then you need to check for null instead of DBNull.Value
If you do have a null value you need to handle, the above code also works for double?
Obviously use your commented out code instead which uses parameters properly, instead of the code you actually have. Please note that AddWithValue is evil, you should specify the actual SqlDbType, although in the case of int it doesn't really matter.
It looks like you are caching your connection object. You should not do this. Instead, create it on the spot, and dispose it in a using block.
I need to perform an update to a View that has multiple underlying tables using the ExecuteCommand method of a DataContext. I am using this method because of the known restriction of linqToSQL when performing this type of operation on Views having multiple underlying tables.
My existing SQL statement is similar to the following where I am setting newFieldID to a null value simply for this post to illustrate the issue. In the application, newFieldID is assigned a passed parameter and could actually be an integer value; but my question is specific to the case where the value being provided is a null type:
using (var _ctx = new MyDataContext())
{
int? newFieldID = null;
var updateCmd = "UPDATE [SomeTable] SET fieldID = " + newFieldID + "
WHERE keyID = " + someKeyID;
try
{
_ctx.ExecuteCommand(updateCmd);
_ctx.SubmitChanges();
}
catch (Exception exc)
{
// Handle the error...
}
}
This code will fail for the obvious reason that the updateCmd won't be completed in the case of a null value for the newFieldID. So how can I replace or translate the CLR null value with an SQL null to complete the statement?
I know I could move all of this to a Stored Procedure but I am looking for an answer to the existing scenario. I've tried experimenting with DBNull.Value but aside from the challenge of substituting it for the newFieldID to use in the statement, simply placing it into the string breaks the validity of the statement.
Also, enclosing it within a single quotes:
var updateCmd = "UPDATE [SomeTable] SET fieldID = '" + DBNull.Value + "'
WHERE keyID = " + someKeyID;
Will complete the statement but the value of the field is translated to an integer 0 instead of an SQL null.
So How does one go about converting a CLR null or nullable int to an SQL Null value given this situation?
Correct way to do it: use override of ExecuteCommand accepting not only command text, but also array of parameters and use parameterized query instead of command string concatenation:
var updateCmd = "UPDATE [SomeTable] SET fieldID = {0} WHERE keyID = {1}";
_ctx.ExecuteCommand(updateCmd, new [] {newFieldID, someKeyID});
It will not only prevent you from sql injection, but also it will do following for you (from MSDN description):
If any one of the parameters is null, it is converted to DBNull.Value.
Try checking newFieldID == null and change the statement accordingly.
Something like below or using separate if / else statement.
var updateCmd = "UPDATE [SomeTable] SET fieldID =" + (newFieldID == null ? "null" : Convert.ToString(newFieldID)) + " WHERE keyID = " + someKeyID;
Normally, when using Stored Procedures or Prepared Statements, you use Parameters to assign values. When you have a DbParameter, you can assign null or DBNull.Value to the Value-Property or your parameter.
If you want to have the null as text in the statement, simply use the SQL-keyword NULL
var updateCmd = "UPDATE [SomeTable] SET fieldID = NULL WHERE keyID = " + someKeyID;
As pointed out by Andy Korneyev and others, a parameterized array approach is the best and probably the more appropriate method when using Prepared statements. Since I am using LinqToSQL, the ExecuteCommand method with the second argument which takes an array of parameters would be advised but it has the following caveats to its usage.
A query parameter cannot be of type System.Nullable`1[System.Int32][] (The main issue I'm trying to resolve in this case)
All parameters must be of the same type
Shankar's answer works although it can quickly become very verbose as the number of parameters could potentially increase.
So my workaround for the problem involve somewhat of a hybrid between the use of parameters as recommended by Andy and Shankar's suggestion by creating a helper method to handle the null values which would take an SQL statement with parameter mappings and the actual parameters.
My helper method is:
private static string PrepareExecuteCommand (string sqlParameterizedCommand, object [] parameterArray)
{
int arrayIndex = 0;
foreach (var obj in parameterArray)
{
if (obj == null || Convert.IsDBNull(obj))
{
sqlParameterizedCommand = sqlParameterizedCommand.Replace("{" + arrayIndex + "}", "NULL");
}
else
sqlParameterizedCommand = sqlParameterizedCommand.Replace("{" + arrayIndex + "}", obj.ToString());
++arrayIndex;
}
return sqlParameterizedCommand;
}
So I can now execute my statement with parameters having potential null values like this:
int? newFieldID = null;
int someKeyID = 12;
var paramCmd = "UPDATE [SomeTable] SET fieldID = {0} WHERE keyID = {1}";
var newCmd = PrepareExecuteCommand(paramCmd, new object[] { newFieldID });
_ctx.ExecuteCommand(newCmd);
_ctx.SubmitChanges();
This alleviates the two previously referenced limitations of the ExecuteCommand with a parameter array. Null values get translated appropriately and the object array may varied .NET types.
I am marking Shankar's proposal as the answer since it sparked this idea but posting my solution because I think it adds a bit more flexibility.
I'm developing a C# .NET Framework 4.0 library.
I have this code:
public static byte GetBatchStatus(string connString)
{
if (string.IsNullOrEmpty(connString))
throw new ArgumentNullException("connString");
byte status;
using (System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection(connString))
{
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.CommandText = GetBatchStatusValueSQL;
cmd.CommandType = CommandType.Text;
cmd.Connection = conn;
object o = cmd.ExecuteScalar();
// Throws an ArgumentNullException if o is null.
if (o == null)
throw new ArgumentNullException("o");
status = Convert.ToByte(o);
}
return status;
}
cmd.ExecuteScalar(); could return a null, but Convert.ToByte(o); returns 0.
If cmd.ExecuteScalar(); returns null its an error, because the value I'm looking for must be on database. If that value is not on database is an error.
What would you do here? Return a null or throw an exception?
You are pretty much answering your own question:
because the value I'm looking for must be on database. If that value is not on database is an error.
If your program doesn't function without that value you should throw an exception, if not you can return null and make the user of the library decide what to do next.
I think if you want to do something if cmd.ExecuteScalar() returns null then you should return a null. But as you said
the value I'm looking for must be on database. If that value is not on database is an error.
then you should throw an exception type of InvalidOperationException rather than ArgumentNullException.
If that value is not on database is an error.
An error in terms of "my belief system about the state of the system has been violated" or "the input must be invalid somehow"? It sounds like it's more the former - so I'd throw an exception. It sounds like the caller can't reasonably continue in this case. If they can, that's a different matter.
You might want to use InvalidOperationException, or perhaps create your own exception (InvalidDatabaseStateException for example) given that it's not really the state of this object which is invalid.
According to here http://msdn.microsoft.com/en-us/library/ms229009(v=vs.110).aspx and here http://msdn.microsoft.com/en-us/library/ms229030(v=vs.110).aspx exceptions are very expensive and should be used with care.
I surely would use return Convert.ToByte(o) and test it on the calling function.
I run code that produces a datatable. From this datatable, I gleen the State & CRID. I use these pieces of information to run code to get the PRID. Problem is, every time I run this, I get a Null Reference Exception. I've tried declaring int PRID = 0 before running the code; however, I then end up with 0 as my answer every time. I've executed the code in SQLServer 2008 using the same parameters, and I get the correct result.
I am unable to determine why this code is not running correctly.
public int GetPRID(int RRID, string State, int PCID)
{
try
{
SQLCON = new SqlConnection(connectionString);
SQLCON.Open();
SQLCmd = new SqlCommand("spGetPRID", SQLCON);
SQLCmd.CommandType = CommandType.StoredProcedure;
SQLCmd.Parameters.Add("#RRID", SqlDbType.Int).Value = RRID;
SQLCmd.Parameters.Add("#State", SqlDbType.VarChar).Value = State;
SQLCmd.Parameters.Add("#PCID", SqlDbType.Int).Value = PCID;
int PRID = (Int32)SQLCmd.ExecuteScalar();
return PRID;
}
catch(Exception ex)
{
HttpContext.Current.Response.Redirect("~/ErrorRedirect.aspx?" + ex.Message, false);
return 0;
}
finally
{
SQLCON.Close();
}
}
This line is problematic here
int PRID = (Int32)SQLCmd.ExecuteScalar();
of course I can't know the result of your stored procedure, but ExecuteScalar could return NULL and if this is the case the cast to Int32 will fail with the error null reference exception
MSDN says
Returns the first column of the first row in the result set, or a null
reference (Nothing in Visual Basic) if the result set is empty
So the correct approach if there is a possibility to get a null as return value is
object result = SQLCmd.ExecuteScalar();
if(result != null)
PRID = Convert.ToInt32(result);
From memory SQLCmd.ExecuteScalar() will return null if no value is returned, which will give you a null reference exception. If a value is returned but the value is a database null it will return BDNull.value, which will also fail because it can't be cast to an int32.
I found the issue. It wasn't in my C# code - it was within the SQL itself. I was declaring #PRID as int, and asking it to return #PRID.
Once I removed this, it worked fine. Thank you all for your contributions.
I have a SQL table with a column of type nvarchar(20) and want to read that column using SqlDataReader. Looks like the only way to do this is to use GetSqlChars() followed by ToSqlString():
String result = reader.GetSqlChars(index).ToSqlString().Value
the problem is that if the stored value happens to be null (and that's valid for my case) I get
[SqlNullValueException: Data is Null. This method or property cannot be called on Null values.]
System.Data.SqlTypes.SqlString.get_Value() +3212527
so I have to first check what the value returned by ToSqlString() returns in IsNull():
SqlString asSqlString = reader.GetSqlChars(index).ToSqlString();
String result = asSqlString.IsNull() ? null : asSqlString.Value;
which works but requires lots of extra code and looks really inelegant.
Is there a more elegant way to achieve the same effect?
Perhaps:
var value = reader.IsDBNull(index) ? null : reader.GetString(index);
Or even shorter:
var value = reader[index] as string;
You can use the GetValue() method:
// define your query
string query = "SELECT YourField FROM dbo.YourTable WHERE ID = 1";
using(SqlConnection conn = new SqlConnection("......"))
using(SqlCommand cmd = new SqlCommand(query, conn))
{
conn.Open();
using(SqlDataReader rdr = cmd.ExecuteReader())
{
if(rdr.Read())
{
string fieldValue = rdr.GetValue(2).ToString();
}
}
conn.Close();
}
GetString method is specifically coded to throw an exception if the database value is null. This is by design.
The docs for GetString state:
Call IsDBNull to check for null values before calling this method
On the other hand, if you use GetValue() and you end up with a DBNull object as your value, the DBNull.ToString method automatically returns String.Empty.