Как сделать запрос по составным ключам, используя ORM + LINQ?
У меня есть массив объектов в памяти, которые представляют составные ключи, которые я хочу запросить, например:
public class Key
{
public string Part1 {get;set;}
public string Part2 {get;set;}
}
Теперь скажи, что у меня есть IQueryable<Table1>
и я хочу вернуть все записи с вышеуказанным Key
s. Таблица 1 не имеет суррогатного ключа, она имеет составной ключ, состоящий из 2 столбцов. Я также хочу, чтобы избежать для каждой строки поиска.
Как я могу это сделать? Я пробовал стандартное объединение, но по понятным причинам мой ORM сбивается с толку, когда в памяти выбрасываются объекты.
2 ответа
В подобных случаях я обнаружил, что лучшим решением будет первое, в котором выполняется выбор в базе данных, а затем точные совпадения в памяти:
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;
Если у вас есть ключи
1,2
2,1
затем dbSelection
также будет содержать {1,1
} а также {2,2}
(но не подавляющее большинство других записей). Они отфильтрованы вторым запросом.
Преимущество состоит в том, что запрос к базе данных может использовать индексы, что невозможно, если вы работаете с вычисленными ключами (например, значениями сцепленных ключей).
Недостатком является то, что вы должны убедиться, что parts1
а также parts2
не может расти чрезмерно, иначе SQL IN
операторы станут крайне неэффективными или даже потерпят крах из-за слишком большого количества элементов (мы говорим о многих тысячах пунктов здесь для Sql Server). Но это верно для любого решения, использующего Contains
,
Обходной путь мог работать с конкатенацией
что-то вроде того.
var keys = new List<Key> {
{new Key {Part1="12", Part2="3"},
{new Key {Part1="1", Part2="23"}
};
var concatenatedKeys = keys.Select(m => m.Part1 + "~" + m.Part2).ToList();
var queryable = repository.MyEntity.Where(x => concatenatedKeys.Contains(x.CompositeKey1 + "~" + x.CompositeKey2);
Разделитель (~
в данном случае) является произвольным, то есть символом, который не будет найден в ваших строках.
Это поможет избежать "неправильных" совпадений (например, 12 и 3 против 1 и 23)