Reputation: 149
I am trying to figure out how to make a query in LINQ where some values are nullable.
Next is my table:
ID int
Key1 int
Key2 int?
Key3 int?
Value string
uniq = Key1+Key2+Key3+Value
Now I need to check if an existing record is there already base on a unique constraint.
I tried the following:
Object tmp = model.table.FirstOrDefault(row => row.Key1 == newItem.Key1 &&
(row.Key2.HasValue && newItem.Key2.HasValue && row.Key2.Value == newItem.Key2.Value) &&
(row.Key3.HasValue && newItem.Key3.HasValue && row.Key3.Value == newItem.Key3.Value) &&
row.Value == newItem.Value);
and:
Object tmp = model.table.FirstOrDefault(row => row.Key1 == newItem.Key1 &&
row.Key2 == newItem.Key2 &&
row.Key3 == newItem.Key3 &&
row.Value == newItem.Value);
But both didn't get me correct results when one of the keys is null!
Is there a way to write a correct LINQ query?
Upvotes: 1
Views: 2247
Reputation: 7471
Some time ago, I wrote a small function to handle these kind of situations:
Private Function EqualOrBothNull(ByVal int1 As Int32?, ByVal int2 As Int32?) As Boolean
Select Case True
Case (int1 Is Nothing AndAlso int2 Is Nothing)
Return True
Case (int1 Is Nothing AndAlso int2 IsNot Nothing) OrElse (int1 IsNot Nothing AndAlso int2 Is Nothing)
Return False
Case (int1 IsNot Nothing AndAlso int2 IsNot Nothing)
Return int1 = int2
End Select
End Function
It's in VB.NET, but it should be simple to convert it in C#:
private bool EqualOrBothNull(Nullable<Int32> int1, Nullable<Int32> int2) {
switch (true) {
case (int1 == null && int2 == null):
return true;
case (int1 == null && int2 != null) || (int1 != null && int2 == null):
return false;
case (int1 != null && int2 != null):
return int1 == int2;
}
}
Then, you can simply write:
Object tmp = model.table.FirstOrDefault(row =>
EqualOrBothNull(row.Key1, newItem.Key1) &&
EqualOrBothNull(row.Key2, newItem.Key2) &&
EqualOrBothNull(row.Key3, newItem.Key3) &&
EqualOrBothNull(row.Value, newItem.Value));
To reply to the several comments to my answer:
At least in VB.NET, a comparison of two Nullable(Of T) always evaluates to a Boolean? with value of Nothing if at least one of them has NO value:
And NO, a = b
is not only an assignment in VB.NET.
The operator =
is context-base auto-switched by the compiler between assignment and comparison.
Moreover, in Quickwatch mode, it's always parsed as a comparison.
C# behaves differently from VB:
Just checked, in C# the simple ==
operand acts like you would expect (like Ilya Ivanov and Konrad Morawski turned out in comments), so there is no need to use HasValue in this question's scenario.
Upvotes: 1
Reputation: 302
object tmp= model.table.FirstOrDefault(t =>
t.Key1 == newItem.Key1
&& ((!t.Key2.HasValue & !newItem.Key2.HasValue)
| t.Key2.Value == newItem.Key2.Value)
&& ((!t.Key3.HasValue & !newItem.Key3.HasValue)
| t.Key3.Value == newItem.Key3.Value) && t.Value == newItem.Value);
Upvotes: 2