0

I wrote a generic method to retrieve single values from database (MSSQL Server).
I encountered into a case that I need to get a Boolean value from DB.

As you can see in the code below, a Object local field (IsExist) gets the result.

When the value in DB is False GenericScalar() method return False (as it should) and the condition: if (IsExist == null) in GetWanLineDisconnectionData() is true and the return block is executing, even though IsExist is False and not null.

Why is that?

How can I overcome this problem?

private void GetWanLineDisconnectionData()
{
    string q = "SELECT WanLineDiscconection FROM AdditionalProjectsData WHERE SpCall= " + "'" + spCall + "'";
    object IsExist = Orange.ProjectManagment.DAL.Database.GenericScalar<object>(q);
    if (IsExist == null) {
        return;
    }
    if (bool.Parse(IsExist) == true) {
        RadWanDiscYes.Checked = true;
    } else {
        RadWanDiscNo.Checked = true;
    }    
}

Database method:

public static T GenericScalar<T>(string query)
{
    OleDbConnection connection = new OleDbConnection(sqlConnString);
    connection.Open();

    OleDbCommand cmd = new OleDbCommand(query, connection);
    try
    {
        var result = cmd.ExecuteScalar();

        if (result == null)
        {
            return default(T);
        }
        else
        {
            return (T)result;
        }
    }

    catch (Exception ex)
    {
        throw ex;
    }
    finally
    {
        CloseConnection(ref connection);
    }
}

EDIT:
maybe a few screen shoots will better demonstrate it: (note that: GetWanLineDisconnectionData() is written in VB.NET and GenericScalar() is written in C# on a different project in the solution):

  1. in the beginning IsExist is nothing (null). enter image description here
  2. the query finds one row and the value of the bool column WanLineDiscconection is false and IsExist is set to false as it should be. enter image description here

  3. here is the problem, the program enters the if block and IsExist is not nothing (null). enter image description here

Jonathan Applebaum
  • 5,738
  • 4
  • 33
  • 52
  • 1
    Its null for no value but DBNull.Value for a db NULL btw. – Alex K. Aug 02 '17 at 14:15
  • 1
    Please use `using` blocks, your snippet is a textbook sample of how to mess it up by trying to do it yourself. And don't `throw ex;`. Use `throw;` only, the alternative messes up the stack trace. – nvoigt Aug 02 '17 at 14:15
  • What is the type of `WanLineDiscconection`? – MakePeaceGreatAgain Aug 02 '17 at 14:48
  • @nvoigt True, but in this case it would be better to omit the catch block altogether as it serves no function (except destroying the stack trace). – ckuri Aug 02 '17 at 18:53
  • @HimBromBeere its boolean – Jonathan Applebaum Aug 03 '17 at 08:11
  • So why not use `GenericScalar` if you know the type returned from the database? Then `default(T)` will evalauet to `false`. – MakePeaceGreatAgain Aug 03 '17 at 08:25
  • @HimBromBeere, that what i did in the beginning, so when the column is null default(T) returns false and not null (as it should, false is default of boolean) so i have changed the data type to an object because i need null and not false, please see my last comment to CodeCaster – Jonathan Applebaum Aug 03 '17 at 08:45

2 Answers2

1

The variable foo in object foo = false is definitely not null, so the premise in your title is incorrect.

ExecuteScalar() returns null when there are no rows. In that case, the method GenericScalar() return default(T), which for object will be null.

How to solve this depends on what your data looks like. You probably want to represent the result in a nullable int, or int? instead of object:

var exists = Orange.ProjectManagment.DAL.Database.GenericScalar<int?>(q);
RadWanDiscYes.Checked = exists.GetValueOrDefault() > 0;

See How does GetValueOrDefault work?, What is the default value of the nullable type "int?" (including question mark)?.

Aside from that: you generally also don't want to write handy wrappers around database queries, because you're reinventing the wheel poorly, opening up your application to SQL injection. That being said, there's a lot more going wrong in that method, including but not limited to not disposing your database connection and rethrowing exceptions without their stacktrace.

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • Why not use `exists.HasValue`? Seems easier than querying the value and determing if that one is greater zero, doesn´t it? – MakePeaceGreatAgain Aug 02 '17 at 14:32
  • @HimBromBeere as stated in the answer, I don't know what their data looks like. `(int?)0` has a value, but might mean `false`. – CodeCaster Aug 02 '17 at 14:39
  • @CodeCaster thank you for your response and code review notes, but there is a row, and the value of the boolean column `WanLineDiscconection` is false, hence `IsExist` value is false (not null) and it is the expected result of the query, the problem is inside `GetWanLineDisconnectionData()` method: the condition `(IsExist == null)` treat it as null and not as false like it should be and the if block is executed (and it should not be excuted) , i know that sound strange, i have tested it a few times and i cant understand why. – Jonathan Applebaum Aug 03 '17 at 08:09
  • another thing that maybe is important to mention, `GetWanLineDisconnectionData()` is written in VB.NET and `GenericScalar()` is written in C# on a different project in the solution. – Jonathan Applebaum Aug 03 '17 at 08:15
  • @jonathana you're probably debugging a release build, and the debugger is lying about the line that leaves the method. – CodeCaster Aug 03 '17 at 09:22
0

You´re mixing compile-time and runtime information. Whilst GenericScalar<object>(q) is an information that exists at compile-time the type returned from ExecuteScalar at compile-time is just object. You expect it to be of type boolean, which might or might not be true for your specific case. However this is not relevant to the compiler.

In short that means that T actually is object, not whatever you expect it to be from your database. And as CodeCaster allready mentioned the default-value for object is just null.

Si I´d suggest to use GenericScalar<bool> instead of GenericScalar<object> because you seem to know what your query actually returns - in your case a bool. Then default(T) evaluates to false, not to null.

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111