In similar cases I've found the best solution to be one in which a first selection is done in the database, followed by the exact matches in memory:
var parts1 = Keys.Select(k => k.Part1).ToArray();
var parts2 = Keys.Select(k => k.Part2).ToArray();
var dbSelection = context.TableRecords.Where(r => parts1.Contains(r.Part1)
&& parts2.Contains(r.Part2);
var finalSelection = from record in dbSelection
.AsEnumerable() // to memory
join key in Keys on new { record.Part1, record.Part2 }
equals
new { key.Part1, key.Part2 }
select record;
If you've got Keys
1,2
2,1
then dbSelection
will also contain {1,1
} and {2,2}
(but not the vast majority of other records). These are filtered out by the second query.
The advantage is that the database query can take advantage of indexes, which is not possible if you work with computed keys (like concatenated key values).
The downside is that you must ensure that parts1
and parts2
can't grow excessively, otherwise the SQL IN
statements will become highly inefficient or even crash because of too many elements (we're talking many thousands of items here for Sql Server). But this is true for any solution using Contains
.