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.
Related
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;
}
Code:
private void DoSomethingWithDatabase(string f1, int f2)
{
SqlCommand myCommand = new SqlCommand("SELECT Field1,Field2,Field3 FROM MyTable WHERE Field1 = #F1 AND Field2 = #F2", this.myConn);
myCommand.Parameters.Add("#F1", System.Data.SqlDbType.VarChar);
myCommand.Parameters.Add("#F2", System.Data.SqlDbType.Int);
if (f1 == "")
myCommand.Parameters["#F1"].Value = DBNull.Value;
else
myCommand.Parameters["#F1"].Value = f1;
if (f2 < 0)
myCommand.Parameters["#F2"].Value = DBNull.Value;
else
myCommand.Parameters["#F2"].Value = f2;
// code to do stuff with the results here
}
The server is a Microsoft SQL Server instance.
The database table MyTable contains fields which are nullable. Therefore null is a valid value to search on when performing the query.
From my reading, and also from testing code like this, doing something like what I did here doesn't work properly because apparently you can't do an "equals null" comparison this way - you're supposed to do "IS NULL".
It looks like you can correct this and make it work by setting ANSI_NULL to OFF (as per https://msdn.microsoft.com/en-us/library/ms188048.aspx) but it also indicates this method is deprecated and should not be used.
That article suggests you can use an OR operator to do something like WHERE Field1 = 25 OR Field1 IS NULL. The problem is, with a single call to this function, I want to check for either null and only null, or for the given constant value and nothing else.
So far, it seems like I need to basically build the query piece by piece, string by string, to account for the possibility of NULL values. So I'd need to do something like:
string theQuery = "SELECT Field1,Field2,Field3 FROM MyTable WHERE ";
if (f1 == "")
theQuery += "Field1 IS NULL ";
else
theQuery += "Field1 = #F1 ";
// ...
SqlCommand myCommand = new SqlCommand(theQuery, this.myConn);
if (f1 == "")
{
myCommand.Parameters.Add("#F1", System.Data.SqlDbType.VarChar);
myCommand.Parameters["#F1"].Value = f1;
}
// ...
Is this really how it needs to be done? Is there a more efficient way to do this without repeating that if block and by using parameters rather than concatenating a query string together?
(Notes: An empty string is converted to a NULL here for the example. In the scenario I'm actually working with, empty strings are never used, but instead NULL is stored. The database is out of my control, so I can't just say "change all your NULLs to empty strings". Same goes for the ints - if we pass, say, -1 into the function it should be testing for a null value. Bad practice? Maybe, but the database itself is not in my control, only the code to access it is.)
Why not using:
string theQuery = "SELECT Field1, Field2, Field3 FROM MyTable WHERE ISNULL(Field1,'') = #F1";
?
That way you get rid of your if block and your null values are interpreted as an empty string, like your f1 variable.
SQL Server has a function called ISNULL(Column,Value) where you can specify one column to check and set a Default Value in case this Column is NULL.
You can check here
You could do something like
WHERE ISNULL(Field, '') = #F1
In that case, NULL fields are treated like empty strings. Another way would be:
WHERE Field IS NULL OR Field = #1
The way you are dealing with NULLs is the right way. Maybe you could use a helper method like this one:
private string AppendParameter(string query, string parameterName, string parameterValue, SqlParameterCollection parameters)
{
if (string.IsNullOrEmpty(parameterValue))
query += "Field1 IS NULL ";
else
{
query += "Field1 = " + parameterName + " ";
parameters.AddWithValue(parameterName, parameterValue);
}
return query;
}
SqlCommand myCommand = new SqlCommand(#"SELECT Field1,Field2,Field3
FROM MyTable
WHERE
( (#F1 IS NULL AND [field1] IS NULL) OR [field1] = #F1 ) AND
( (#F2 IS NULL AND [field2] IS NULL) OR [field2] = #F2 );", this.myconn);
myCommand.Parameters.Add("#F1", System.Data.SqlDbType.VarChar).Value = DBNull.Value;
myCommand.Parameters.Add("#F2", System.Data.SqlDbType.Int).Value = DBNull.Value;
if (!string.IsNullOrEmpty(f1))
myCommand.Parameters["#F1"].Value = f1;
if (f2 != -1)
myCommand.Parameters["#F2"].Value = f2;
This would utilize indexes on fields.
If you want to avoid if blocks, you can probably change the query into something like this:
SELECT Field1,Field2,Field3 FROM MyTable
WHERE COALESCE(Field1,'') = #F1 AND COALESCE(Field2,-1) = #F2
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 getting a strange error in Visual Studio, and of course this great software is unable to tell me where the error is, just that I'm getting an error. I guess the best I can do is paste my code.
using (SQLiteCommand cmd = new SQLiteCommand(query, con))
{
using (SQLiteDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
//Console.WriteLine("{0} ", rdr["logLnNum"]);
ulong start, end, delta = 0;
string contentStr;
string contentMarkup;
String group;
start = (ulong)rdr["startTime"];
end = (ulong)rdr["endTime"];
convertTimes(start, end, 2728232, delta);
contentStr = String.Format("{0}, {1}, {2}, {3}, {4} (ms)",
rdr["offsetOfData"], rdr["amountOfData"], rdr["filename"],
rdr["logLnNum"], (delta * .001));
contentMarkup = "<div title=\"" + contentStr + "\">" + contentStr + "</div>";
group = String.Format("{0:X}", rdr["threadId"]);
group = group + ", " + rdr["threadName"];
TimelineData inputData = new TimelineData(contentMarkup, end, group, start);
Console.WriteLine("Data processed");
dataSet.Add(inputData);
}
}
}
Again, the only error I get is "System.InvalidCastException" occurred in .exe.
Direct casting from an object only works when that object inherits from the type you're casting to (somewhere along the line, anyways).
A simple way to get the type you need out of a DataReader is to call
[type].Parse(reader[index].ToString())
where [type] is what you want to cast to, i.e.
ulong.Parse(rdr["startTime"].ToString())
The DataReader objects typically have a .GetInt32(int), .GetDecimal(int), etc. that simply requires you to pass in the index of the column to parse. If you only have the name of the column, you can use Reader.GetOrdinal("yourColumnName").
I would like to recommend you using an extra method to delimit this kind of errors.
For instance, consider this code:
protected T getDataValue<T>(object value)
{
if (value != null && DBNull.Value != value)
return (T)value;
else
return default(T);
}
Then call it inside your datareader iteration for each value retrieved, this will help you during debugging to detect which field is producing the exception.
Example:
start = getDataValue<ulong>(rdr["startTime"]);
end = getDataValue<ulong>(rdr["endTime"]);
In a nutshell, I usually follow these guidelines when working with data access to avoid exceptions:
If you have access to database, check your data using a db client before executing queries from ADO, this can be done executing the same query.
Consider using the same types in both layers (DB and App), in order to avoid cast exceptions --> it's not the same to convert from varchar to int, that making a simple cast from int to int (of course it comes as an object but for sure it has a type expected value), you reduce a lot of validation logic when using the same types.
If your table's fields accept nulls, consider using a strategy of conversion (like the method I gave you) to assign a valid value when null.
Try to avoid writing too much "logic" in DataAccess methods, just keep it simple, you can use a DTO or a business class in order to store all the values you've got from db, then use it/modify/create a logic in the business layer.
Hope it helps
When the Address2 column is empty, this line:
string address2 = sqlD8aReader.GetString(ADDRESS2_OFFSET);
...fails with:
System.Data.SqlTypes.SqlNullValueException was unhandled by user code
HResult=-2146232015
Message=Data is Null. This method or property cannot be called on Null values.
Source=System.Data
StackTrace:
at System.Data.SqlClient.SqlBuffer.get_String()
at System.Data.SqlClient.SqlDataReader.GetString(Int32 i)
at HandheldServer.Models.SQLServerPOCRepository..ctor() in c:\HandheldServer\HandheldServer\Models\SQLServerPOCRepository.cs:line 58
InnerException:
How can I safely read strings that might be null? Should it be dealt with in SQL somehow (if so, how?) or should it be dealt with in the reading/C# code?
I don't know how your data is being populated, but often times when we execute a query where I work, if it's imperative to have empty strings instead of null for certain operations, we'll use ISNULL(column, '') on columns that might possibly return null so our application layer won't need to worry about whether or not the value is null, it will just handle it like a string.
Otherwise, if you want to handle it application side you can use this:
if(!sqlD8aReader.IsDBNull(ADDRESS2_OFFSET))
{
string address2 = sqlD8aReader.GetString(ADDRESS2_OFFSET);
}
A quick one-liner would be
string address2 = sqlD8aReader.IsDBNull(ADDRESS2_OFFSET)
? null
:sqlD8aReader.GetString(ADDRESS2_OFFSET);
or as an extension method:
public static GetStringOrNull(this IDataRecord dr, int i)
{
return dr.IsDBNull(i)
? null
: dr.GetString(i);
}
and call
string address2 = sqlD8aReader.GetStringOrNull(ADDRESS2_OFFSET);
You can achieve that in your sql query like:
select * from SomeTable where SomeColumn is not null
This way you wouldn't get that exception. But you should also check if the returned value is null or not like akousmata said.
Handling it from the DB side
1) Make sure Address is not NULL when inserting
INSERT INTO t(Address) VALUES(ISNULL(#Address, ''))
2) Return empty string instead if NULL
SELECT ISNULL(Address, '') as Address FROM t
And you already had good answers from other users sqlReader.IsDBNull
The easiest/Best solution is to make your SQL query print other values instead of null. For example, If I have a column1 where I probably get null values, I would call this query :
SELECT isnull(column1,0) FROM table
which prints 0 instead of null values
Instead of calling this query :
SELECT column1 FROM table
which prints the null values and throws exceptions when retreiving data with C#.