简体   繁体   中英

A strange thing about Nullable Boolean type, And and Or in C#

I have a strange problem about the following codes:

!isActive.HasValue || (isActive.HasValue && x.IsActive == isActive.Value)

isActive is a bool? type, so if isActive is null , then I will get the following error:

Nullable object must have a value.

Any idea about that? Thanks for your help!

Update 1:

public static List<User> Select(int userId, bool? isActive = null)
{
    var dl = DataLayer.GetDataContext();
    return dl.Users.Where(x => x.ID == userId
                && (!isActive.HasValue || (isActive.HasValue && x.IsActive == isActive.Value))).ToList();
}

This is the sample function I got this error. I know it is so strange! If I change like below, it will work:

!isActive.HasValue || (isActive.HasValue && x.IsActive == isActive)

I understand this error mean, but I don't understand why. It should work I think. So I call it strange thing!

Update 2:

  1. x.IsActive is bool type, not bool?. I am sure on it :)
  2. DataLayer.GetDataContext() is for Linq to SQL.

Is it possible it is because I am using SQL Server?

Thanks guys for your help so far.

You have two different isActive values in your code, so it looks like you're missing the x on the second check:

!isActive.HasValue || (x.isActive.HasValue && (x.IsActive == isActive.Value))
                   ----^

I've also added parentheses around the equality check just to eliminate any confusion around the order or operations

Since it's Linq-to-SQL, then perhaps it's not short-circuiting or evaluating the expression parts in the proper order. I haven't used it, but from what I understand it reads the content of your lambda expressions and converts it to a close equivalent SQL statement; it does not actually execute it like C# code.

Even so, doing the check for each item seems like a bit much when the value is fixed. You could rewrite the method as such:

public static List<User> Select(int userId, bool? isActive = null)
{
    var dl = DataLayer.GetDataContext();
    var users = dl.Users.Where(x => x.ID == userID);

    if (!isActive.HasValue)
    {
        return users.ToList();
    }
    else
    {
        bool isActiveValue = isActive.Value;
        return users.Where(x => x.IsActive == isActiveValue).ToList();
    }
}

This way there's no additional constraints on the generated SQL query if they're not applicable. EDIT: Updated it so in addition, there are no nullable booleans being passed into your query whatsoever.

Oh, it's because of the query builder in LINQ. There may be a way to execute your intention in one query (in fact it sounds like your updated code in the q does just that), but the straightforward thing to do is to split it into two queries since you know the value of isActive beforehand. That way you don't have to worry about what the query builder may or may not be doing.

For that matter you may as well just make this two separate functions entirely, just to make it harder for future maintainers to accidentally fall into the same trap:

public static List<User> Select(int userId)
{
    return DataLayer.GetDataContext().Users.Where(x => x.ID == userId).ToList();
}

public static List<User> Select(int userId, bool isActive)
{
    return DataLayer.GetDataContext().Users.Where(x => x.ID == userId && x.IsActive == isActive).ToList();
}

You're code shouldn't be failing on that line.

I'm guessing it is failing somewhere in your if block (I'm assuming that's what this statement is for). Your first condition will return true if isActive is null . If you then try to use isActive from inside the block, you'll get the Nullable error.

Where does the code fail if you do this?

public static List<User> Select(int userId, bool? isActive = null)
{
    var dl = DataLayer.GetDataContext();
    return dl.Users.Where(x => E(userId, isActive, x)).ToList();
}

static bool E(int userId, bool? isActive, User x)
{
    return x.ID == userId && D(isActive, x);
}

static bool D(bool? isActive, User x)
{
    return (!isActive.HasValue || C(isActive, x));
}

static bool C(bool? isActive, User x)
{
    return (isActive.HasValue && B(isActive, x));
}

static bool B(bool? isActive, User x)
{
    return x.IsActive == isActive.Value;
}

将其类型从bool更改为bool?

bool? IsActive = false;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM