When I try to get an Integer from my SQLite db I can only get it working by reading it as a string and then run int.Parse on it.
Is this right, I read something about this having to do with ExeculeScalar possibly giving back null?
Here is my current code SendSQLExecScalar() sends the command string etc. and return an object
public object SendSQLExecScalar(string C)
{
OpenConnection();
SQLiteCommand SQLCommand = new SQLiteCommand(C, DbConnection);
try
{
object Output = SQLCommand.ExecuteScalar();
CloseConnection();
return Output;
}
catch (Exception X)
{
MessageBox.Show(X.Message);
return null;
}
}
And:
int ID = int.Parse(SendSQLExecScalar(C).ToString());
EDIT :
Specified cast is not valid.
public static int GetImageID(string Path)
{
string C = "SELECT ID FROM Images WHERE Path LIKE '" + Path + "' LIMIT 1";
return ConvertFromDBVal<int>(SendSQLExecScalar(C));
}
public static T ConvertFromDBVal<T>(object obj)
{
if (obj == null || obj == DBNull.Value)
{
return default(T);
}
else
{
return (T)obj; //breaks here saying this cast is invalid
}
}
I read something about this having to do with execuleScalar possibly
giving back null?
Yes, if there is no data that your sql query returns, ExecuteScalar returns null reference.
If you are 100% sure the return value on the first column of the first row is already int, you can just cast it like;
int ID = (int)SendSQLExecScalar(C);
To prevent null cases on this method, I almost always uses rein's generic method as;
public static T ConvertFromDBVal<T>(object obj)
{
if (obj == null || obj == DBNull.Value)
{
return default(T); // returns the default value for the type
}
else
{
return (T)obj;
}
}
Use TryParse instead of Parse, this allows you to test whether something is parseable.
If you use int.Parse() with an invalid int, you'll get an exception while in the TryParse, it returns a boolean letting you know whether the parse succeeded or not.
In short use Parse if you are sure the value will be valid; otherwise use TryParse.
int number = int.Parse(someString);
int number;
int.TryParse(someString, out number);
(int)(long)SendSQLExecScalar(C);
Solved with this, looks Like SQLite integer will return a long object which I needed to unpack before casting it to an int.
Related
I got the above error in my app. Here is the original code
public string GetCustomerNumber(Guid id)
{
string accountNumber =
(string)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidmyApp,
CommandType.StoredProcedure,
"GetCustomerNumber",
new SqlParameter("#id", id));
return accountNumber.ToString();
}
I replaced with
public string GetCustomerNumber(Guid id)
{
object accountNumber =
(object)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidCRM,
CommandType.StoredProcedure,
"spx_GetCustomerNumber",
new SqlParameter("#id", id));
if (accountNumber is System.DBNull)
{
return string.Empty;
}
else
{
return accountNumber.ToString();
}
}
Is there a better way around this?
With a simple generic function you can make this very easy. Just do this:
return ConvertFromDBVal<string>(accountNumber);
using the function:
public static T ConvertFromDBVal<T>(object obj)
{
if (obj == null || obj == DBNull.Value)
{
return default(T); // returns the default value for the type
}
else
{
return (T)obj;
}
}
A shorter form can be used:
return (accountNumber == DBNull.Value) ? string.Empty : accountNumber.ToString()
EDIT: Haven't paid attention to ExecuteScalar. It does really return null if the field is absent in the return result. So use instead:
return (accountNumber == null) ? string.Empty : accountNumber.ToString()
ExecuteScalar will return
null if there is no result set
otherwise the first column of the first row of the resultset, which may be DBNull.
If you know that the first column of the resultset is a string, then to cover all bases you need to check for both null and DBNull. Something like:
object accountNumber = ...ExecuteScalar(...);
return (accountNumber == null) ? String.Empty : accountNumber.ToString();
The above code relies on the fact that DBNull.ToString returns an empty string.
If accountNumber was another type (say integer), then you'd need to be more explicit:
object accountNumber = ...ExecuteScalar(...);
return (accountNumber == null || Convert.IsDBNull(accountNumber) ?
(int) accountNumber : 0;
If you know for sure that your resultset will always have at least one row (e.g. SELECT COUNT(*)...), then you can skip the check for null.
In your case the error message "Unable to cast object of type ‘System.DBNull’ to type ‘System.String`" indicates that the first column of your result set is a DBNUll value. This is from the cast to string on the first line:
string accountNumber = (string) ... ExecuteScalar(...);
Marc_s's comment that you don't need to check for DBNull.Value is wrong.
You can use C#'s null coalescing operator
return accountNumber ?? string.Empty;
This is the generic method that I use to convert any object that might be a DBNull.Value:
public static T ConvertDBNull<T>(object value, Func<object, T> conversionFunction)
{
return conversionFunction(value == DBNull.Value ? null : value);
}
usage:
var result = command.ExecuteScalar();
return result.ConvertDBNull(Convert.ToInt32);
shorter:
return command
.ExecuteScalar()
.ConvertDBNull(Convert.ToInt32);
There is another way to workaround this issue. How about modify your store procedure? by using ISNULL(your field, "") sql function , you can return empty string if the return value is null.
Then you have your clean code as original version.
I suppose you can do it like this:
string accountNumber = DBSqlHelperFactory.ExecuteScalar(...) as string;
If accountNumber is null it means it was DBNull not string :)
String.Concat transforms DBNull and null values to an empty string.
public string GetCustomerNumber(Guid id)
{
object accountNumber =
(object)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidCRM,
CommandType.StoredProcedure,
"spx_GetCustomerNumber",
new SqlParameter("#id", id));
return String.Concat(accountNumber);
}
However, I think you lose something on code understandability
Since I got an instance which isn't null and if I compared to DBNULL I got Operator '==' cannot be applied to operands of type 'string' and 'system.dbnull' exeption,
and if I tried to change to compare to NULL, it simply didn't work ( since DBNull is an object) even that's the accepted answer.
I decided to simply use the 'is' keyword.
So the result is very readable:
data = (item is DBNull) ? String.Empty : item
based on answer from #rein
public static class DbDataReaderExtensions
{
public static TObjProp Get<TObj, TObjProp>(
this DbDataReader reader,
Expression<Func<TObj, TObjProp>> expression)
{
MemberExpression member = expression.Body as MemberExpression;
string propertyName = member.Member.Name;
//PropertyInfo propInfo = member.Member as PropertyInfo;
var recordOrdinal = reader.GetOrdinal(propertyName);
var obj = reader.GetValue(recordOrdinal);
if (obj == null || obj == DBNull.Value)
{
return default(TObjProp);
}
else
{
return (TObjProp)obj;
}
}
}
Given:
public class MyClass
{
public bool? IsCheckPassed { get; set; }
}
Use as:
var test = reader.Get<MyClass, bool?>(o => o.IsCheckPassed);
or, if you hardcode class type in exception method:
var test = reader.Get(o => o.IsCheckPassed);
p.s. I haven't figured yet how to make generics implicit without sacrificing code length.. fee free to comment and suggest improvements
Full example:
public async Task<MyClass> Test(string connectionString) {
var result = new MyClass();
await using var con = new SQLiteConnection(connectionString);
con.Open();
await using var cmd = con.CreateCommand();
cmd.CommandText = #$"SELECT Id, IsCheckPassed FROM mytable";
var reader = await cmd.ExecuteReaderAsync();
while (reader.Read()) {
// old, not working! Throws exception!
//bool? isCheckPassed1 = reader.GetBoolean(reader.GetOrdinal("IsCheckPassed"));
// old, working, but too long (also if you have like 20 properties then all the more reasons to refactor..)
bool? isCheckPassed2 = null;
bool? isCheckPassed2Temp = reader.GetValue(reader.GetOrdinal("IsCheckPassed"));
if (isCheckPassed2Temp != null && isCheckPassed2Temp != DBNull.Value)
isCheckPassed2 = (bool?)isCheckPassed2Temp;
// new
var isCheckPassed3 = reader.Get<MyClass, bool?>(o => o.IsCheckPassed);
// repeat for 20 more properties :)
result.IsCheckPassed = isCheckPassed3;
}
return result;
}
Solution will work for as long as table column names match property names of the class. And might not be production-grade performance wise, so use or modify at your own risk :)
A more concise approach using more recent C# syntax and also accounting for nullable types:
private static T? FromDbNull<T>(object? obj) =>
obj == null || obj == DBNull.Value ? default : (T)obj;
Can be used with a data reader as follows:
while (reader.Read())
{
var newObject = new SomeObject(
FromDbNull<string?>(reader["nullable_field_1"]),
FromDbNull<string?>(reader["nullable_field_2"]),
FromDbNull<string?>(reader["nullable_field_3"]),
FromDbNull<double?>(reader["nullable_field_4"])
);
response.Add(newObject);
}
I use an extension to eliminate this problem for me, which may or may not be what you are after.
It goes like this:
public static class Extensions
{
public String TrimString(this object item)
{
return String.Format("{0}", item).Trim();
}
}
Note:
This extension does not return null values! If the item is null or DBNull.Value, it will return an empty String.
Usage:
public string GetCustomerNumber(Guid id)
{
var obj =
DBSqlHelperFactory.ExecuteScalar(
connectionStringSplendidmyApp,
CommandType.StoredProcedure,
"GetCustomerNumber",
new SqlParameter("#id", id)
);
return obj.TrimString();
}
Convert it Like
string s = System.DBNull.value.ToString();
I'm trying to build a method that queries a SQL table and assigns the values it finds to a new list of objects. Here's a quick example of how it works (assume the reader and connection are set up and working properly):
List<MyObject> results = new List<MyObject>();
int oProductID = reader.GetOrdinal("ProductID");
int oProductName = reader.GetOrdinal("ProductName");
while (reader.Read())
{
results.Add(new MyProduct() {
ProductID = reader.GetInt32(oProductID),
ProductName = reader.GetString(oProductName)
});
}
There are about 40 other properties too, all of them nullable in the MyObject definition, so I'm trying to keep the assignments as tidy as possible. The problem is that I need to assign null values to the object wherever the reader returns a null. In the above code, if the reader throws a "Data is Null" exception. I'm aware it's possible to use an if statement to check for a DbNull first, but since there are so many properties I'm hoping to keep the code cleaner by not having to spell out an if statement for every single property.
A bit of searching led me to the null-coalescing operator, which seems like it should do exactly what I want. So I tried changing the assignments to look like this:
ProductID = reader.GetInt32(oProductID) ?? null,
ProductName = reader.GetString(oProductName) ?? null
Which works fine for any string but gives me errors of Operator '??' cannot be applied to operands of type 'int' and '<null>' (or any other data type except string. I specifically called out the int (and everything else) as nullable in the object definition, but here it's telling me it can't do that.
The Question
Is there a way to handle nulls in this case that can: (1) Be written clearly in-line (to avoid separate if statements for each property), and (2) Work with any data type?
Null from a database is not "null", it's DbNull.Value. ?? and ?. operators won't work in this case. GetInt32, etc. will throw an exception if the value is null in the DB. I do a generic method and keep it simple:
T SafeDBReader<T>(SqlReader reader, string columnName)
{
object o = reader[columnName];
if (o == DBNull.Value)
{
// need to decide what behavior you want here
}
return (T)o;
}
If your DB has nullable ints for example, you can't read those into an int unless you want to default to 0 or something like. For nullable types, you can just return null or default(T).
Shannon's solution is both overly complicated and will be a performance issue (lots of over the top reflection) IMO.
You can write a series of extensions method for each of the standard GetXXXX. These extensions receive an extra parameter that is the default to return in case the value of the field is null.
public static class SqlDataReaderExtensions
{
public int GetInt32(this SqlDataReader reader, int ordinal, int defValue = default(int))
{
return (reader.IsDBNull(ordinal) ? defValue : reader.GetInt32(ordinal);
}
public string GetString(this SqlDataReader reader, int ordinal, int defValue = "")
{
return (reader.IsDBNull(ordinal) ? defValue : reader.GetString(ordinal);
}
public int GetDecimal(this SqlDataReader reader, int ordinal, decimal defValue = default(decimal))
{
....
}
}
This allows you to leave your current code as is without changes or just change the fields that needs the null as return
while (reader.Read())
{
results.Add(new MyProduct() {
ProductID = reader.GetInt32(oProductID),
ProductName = reader.GetString(oProductName, "(No name)"),
MinReorder = reader.GetInt32(oReorder, null)
.....
});
}
You can also have a version where you pass the column name instead of the ordinal position and do the search for the position inside the extension, but this is probably not good from a performance point of view.
Here's an example that works for fields (can easily be converted to properties) and allows for null checks. It does the dreaded if (in a switch), but it's pretty fast.
public static object[] sql_Reader_To_Type(Type t, SqlDataReader r)
{
List<object> ret = new List<object>();
while (r.Read())
{
FieldInfo[] f = t.GetFields();
object o = Activator.CreateInstance(t);
for (int i = 0; i < f.Length; i++)
{
string thisType = f[i].FieldType.ToString();
switch (thisType)
{
case "System.String":
f[i].SetValue(o, Convert.ToString(r[f[i].Name]));
break;
case "System.Int16":
f[i].SetValue(o, Convert.ToInt16(r[f[i].Name]));
break;
case "System.Int32":
f[i].SetValue(o, Convert.ToInt32(r[f[i].Name]));
break;
case "System.Int64":
f[i].SetValue(o, Convert.ToInt64(r[f[i].Name]));
break;
case "System.Double":
double th;
if (r[f[i].Name] == null)
{
th = 0;
}
else
{
if (r[f[i].Name].GetType() == typeof(DBNull))
{
th = 0;
}
else
{
th = Convert.ToDouble(r[f[i].Name]);
}
}
try { f[i].SetValue(o, th); }
catch (Exception e1)
{
throw new Exception("can't convert " + f[i].Name + " to doube - value =" + th);
}
break;
case "System.Boolean":
f[i].SetValue(o, Convert.ToInt32(r[f[i].Name]) == 1 ? true : false);
break;
case "System.DateTime":
f[i].SetValue(o, Convert.ToDateTime(r[f[i].Name]));
break;
default:
throw new Exception("Missed data type in sql select ");
}
}
ret.Add(o);
}
return ret.ToArray();
}
I am using a routine to read data from Xml files similar to this:
<VerificationSample X1 = "1" X3 = "3" ../>
using this call:
XmlReader reader = XmlReader.Create(path);
reader.ReadToFollowing("VerificationSample");
this.X1 = (double)FileStructure.GetAttributeSafe(reader, "X1", typeof(double)); // exists
this.X2 = (double)FileStructure.GetAttributeSafe(reader, "X2", typeof(double)); // doesn't exist
However, some attributes might not exist so I handle it using this routine inside that attribute reader function definition:
public static object GetAttributeSafe(XmlReader reader, string attributeName, Type objectType)
{
// ..
string value = reader.GetAttribute(attributeName);
if (value != null) // attribute exists
{
if (objectType != typeof (string))
{
var converter = TypeDescriptor.GetConverter(objectType);
returnValue = converter.ConvertFrom(value);
}
else // is already a string and doesn't need to be converted
{
return value;
}
}
else // attribute doesn't exist
{
return "0";
}
}
If the attribute doesn't exist, application pops an error:
Specified cast is not valid
What is my mistake?
Because you are returning "0" , which is a String, and you can't Cast this to double. Use Convert.toDouble(String s) instead.
If the attribute doesn't exist, you return "0", which you are then trying to cast to a double. You need to return a default value of whatever objectType is, perhaps by doing
else // attribute doesn't exist
{
if(objectType.IsValueType)
{
return Activator.CreateInstance(objectType);
}
return null;
}
You will only return null if you try to convert to something non-immutable like a class instead of an int or string, which will blow up your function anyway; it's just there so all code paths return something.
I have got this code that might return no results found - null
String result
string searchUby = "SELECT text FROM rooms WHERE year=#year AND event=#eventAND text=#text AND z is NULL";
SqlCommand sqlcom = new SqlCommand(searchUby, conn);
sqlcom.Parameters.AddWithValue("#event",event.Text);
sqlcom.Parameters.AddWithValue("#text", cb_room.SelectedItem);
sqlcom.Parameters.AddWithValue("#year",klientClass.Year());
conn.Open();
result = sqlcom.ExecuteScalar().ToString(); // on this line ex occurs
conn.Close();
I get this exception:
NullReferenceException: Object reference not set to an instance of an object.
May someone help me with solving this?
Try this:
result = (sqlcom.ExecuteScalar() ?? "").ToString();
If it returns null, the result will be an empty string. You can handle that case by an if-statement and notify some message to the user, such as like this:
object r = sqlcom.ExecuteScalar();
if(r != null) result = r.ToString();
else {
//code to handle the null case here...
}
Your ExecuteScalar() is returning a DBNull. You'll have this issue everywhere you use ExecuteScalar, so you should think of using a Generic Helper Function like below that SO User Rein's has written up in a related Question here.
Just do this:
result = ConvertFromDBVal<string>(sqlcom.ExecuteScalar());
using the generic function:
public static T ConvertFromDBVal<T>(object obj)
{
if (obj == null || obj == DBNull.Value) {
return default(T); // returns the default value for the type
}
else
{
return (T)obj;
}
}
result = sqlcom.ExecuteScalar() !=null ? sqlcom.ExecuteScalar().ToString() : string.Empty;
I got the above error in my app. Here is the original code
public string GetCustomerNumber(Guid id)
{
string accountNumber =
(string)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidmyApp,
CommandType.StoredProcedure,
"GetCustomerNumber",
new SqlParameter("#id", id));
return accountNumber.ToString();
}
I replaced with
public string GetCustomerNumber(Guid id)
{
object accountNumber =
(object)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidCRM,
CommandType.StoredProcedure,
"spx_GetCustomerNumber",
new SqlParameter("#id", id));
if (accountNumber is System.DBNull)
{
return string.Empty;
}
else
{
return accountNumber.ToString();
}
}
Is there a better way around this?
With a simple generic function you can make this very easy. Just do this:
return ConvertFromDBVal<string>(accountNumber);
using the function:
public static T ConvertFromDBVal<T>(object obj)
{
if (obj == null || obj == DBNull.Value)
{
return default(T); // returns the default value for the type
}
else
{
return (T)obj;
}
}
A shorter form can be used:
return (accountNumber == DBNull.Value) ? string.Empty : accountNumber.ToString()
EDIT: Haven't paid attention to ExecuteScalar. It does really return null if the field is absent in the return result. So use instead:
return (accountNumber == null) ? string.Empty : accountNumber.ToString()
ExecuteScalar will return
null if there is no result set
otherwise the first column of the first row of the resultset, which may be DBNull.
If you know that the first column of the resultset is a string, then to cover all bases you need to check for both null and DBNull. Something like:
object accountNumber = ...ExecuteScalar(...);
return (accountNumber == null) ? String.Empty : accountNumber.ToString();
The above code relies on the fact that DBNull.ToString returns an empty string.
If accountNumber was another type (say integer), then you'd need to be more explicit:
object accountNumber = ...ExecuteScalar(...);
return (accountNumber == null || Convert.IsDBNull(accountNumber) ?
(int) accountNumber : 0;
If you know for sure that your resultset will always have at least one row (e.g. SELECT COUNT(*)...), then you can skip the check for null.
In your case the error message "Unable to cast object of type ‘System.DBNull’ to type ‘System.String`" indicates that the first column of your result set is a DBNUll value. This is from the cast to string on the first line:
string accountNumber = (string) ... ExecuteScalar(...);
Marc_s's comment that you don't need to check for DBNull.Value is wrong.
You can use C#'s null coalescing operator
return accountNumber ?? string.Empty;
This is the generic method that I use to convert any object that might be a DBNull.Value:
public static T ConvertDBNull<T>(object value, Func<object, T> conversionFunction)
{
return conversionFunction(value == DBNull.Value ? null : value);
}
usage:
var result = command.ExecuteScalar();
return result.ConvertDBNull(Convert.ToInt32);
shorter:
return command
.ExecuteScalar()
.ConvertDBNull(Convert.ToInt32);
There is another way to workaround this issue. How about modify your store procedure? by using ISNULL(your field, "") sql function , you can return empty string if the return value is null.
Then you have your clean code as original version.
I suppose you can do it like this:
string accountNumber = DBSqlHelperFactory.ExecuteScalar(...) as string;
If accountNumber is null it means it was DBNull not string :)
String.Concat transforms DBNull and null values to an empty string.
public string GetCustomerNumber(Guid id)
{
object accountNumber =
(object)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidCRM,
CommandType.StoredProcedure,
"spx_GetCustomerNumber",
new SqlParameter("#id", id));
return String.Concat(accountNumber);
}
However, I think you lose something on code understandability
Since I got an instance which isn't null and if I compared to DBNULL I got Operator '==' cannot be applied to operands of type 'string' and 'system.dbnull' exeption,
and if I tried to change to compare to NULL, it simply didn't work ( since DBNull is an object) even that's the accepted answer.
I decided to simply use the 'is' keyword.
So the result is very readable:
data = (item is DBNull) ? String.Empty : item
based on answer from #rein
public static class DbDataReaderExtensions
{
public static TObjProp Get<TObj, TObjProp>(
this DbDataReader reader,
Expression<Func<TObj, TObjProp>> expression)
{
MemberExpression member = expression.Body as MemberExpression;
string propertyName = member.Member.Name;
//PropertyInfo propInfo = member.Member as PropertyInfo;
var recordOrdinal = reader.GetOrdinal(propertyName);
var obj = reader.GetValue(recordOrdinal);
if (obj == null || obj == DBNull.Value)
{
return default(TObjProp);
}
else
{
return (TObjProp)obj;
}
}
}
Given:
public class MyClass
{
public bool? IsCheckPassed { get; set; }
}
Use as:
var test = reader.Get<MyClass, bool?>(o => o.IsCheckPassed);
or, if you hardcode class type in exception method:
var test = reader.Get(o => o.IsCheckPassed);
p.s. I haven't figured yet how to make generics implicit without sacrificing code length.. fee free to comment and suggest improvements
Full example:
public async Task<MyClass> Test(string connectionString) {
var result = new MyClass();
await using var con = new SQLiteConnection(connectionString);
con.Open();
await using var cmd = con.CreateCommand();
cmd.CommandText = #$"SELECT Id, IsCheckPassed FROM mytable";
var reader = await cmd.ExecuteReaderAsync();
while (reader.Read()) {
// old, not working! Throws exception!
//bool? isCheckPassed1 = reader.GetBoolean(reader.GetOrdinal("IsCheckPassed"));
// old, working, but too long (also if you have like 20 properties then all the more reasons to refactor..)
bool? isCheckPassed2 = null;
bool? isCheckPassed2Temp = reader.GetValue(reader.GetOrdinal("IsCheckPassed"));
if (isCheckPassed2Temp != null && isCheckPassed2Temp != DBNull.Value)
isCheckPassed2 = (bool?)isCheckPassed2Temp;
// new
var isCheckPassed3 = reader.Get<MyClass, bool?>(o => o.IsCheckPassed);
// repeat for 20 more properties :)
result.IsCheckPassed = isCheckPassed3;
}
return result;
}
Solution will work for as long as table column names match property names of the class. And might not be production-grade performance wise, so use or modify at your own risk :)
A more concise approach using more recent C# syntax and also accounting for nullable types:
private static T? FromDbNull<T>(object? obj) =>
obj == null || obj == DBNull.Value ? default : (T)obj;
Can be used with a data reader as follows:
while (reader.Read())
{
var newObject = new SomeObject(
FromDbNull<string?>(reader["nullable_field_1"]),
FromDbNull<string?>(reader["nullable_field_2"]),
FromDbNull<string?>(reader["nullable_field_3"]),
FromDbNull<double?>(reader["nullable_field_4"])
);
response.Add(newObject);
}
I use an extension to eliminate this problem for me, which may or may not be what you are after.
It goes like this:
public static class Extensions
{
public String TrimString(this object item)
{
return String.Format("{0}", item).Trim();
}
}
Note:
This extension does not return null values! If the item is null or DBNull.Value, it will return an empty String.
Usage:
public string GetCustomerNumber(Guid id)
{
var obj =
DBSqlHelperFactory.ExecuteScalar(
connectionStringSplendidmyApp,
CommandType.StoredProcedure,
"GetCustomerNumber",
new SqlParameter("#id", id)
);
return obj.TrimString();
}
Convert it Like
string s = System.DBNull.value.ToString();