Необходимо получить список элементов, содержащих подэлементы, сопоставленные с другим списком - PullRequest
3 голосов
/ 21 мая 2009

Положение:

У меня есть люди с определенными навыками, и они могут / могут принадлежать к нескольким областям. Навыки связаны в отдельной таблице, так же как и области.

Я получаю список людей, выбрав всех людей с совпадением по каждому навыку, и добавляю их в список, где я могу использовать Distinct (), чтобы убедиться, что они не появляются дважды.

Итоговый список лиц:

List<Person> peopleWithRightSkills

На каждом объекте [Person] у меня есть как минимум 1 связанный адрес, но они могут иметь больше, чем в отношении [Person]

У меня есть другой список:

List<PostalCode> acceptedPostalcodes

Теперь мне нужно сравнить и отфильтровать тех людей с правами навыка, у которых есть адрес, почтовый код которого находится в пределах принятых почтовых кодов

Я исследовал лямбда-выражения, SelectMany среди других решений, но сейчас у меня есть только один вариант, который я считаю «старым стилем» ведения дел, а именно, проходить через каждого персонажа и для каждого совпадения человека ее / его список адресов против списка почтовых индексов. и для каждого совпадения добавьте это к:

List<Person> matchedPeople

Обзор таблицы (сокращение необходимых деталей)

[Table:Person]
int:ID (primary)
string:FirstName
string:LastName

[Table:Address]
int:Person_ID (foreign key to Person)
int:PostalCode_ID (foreing key to PostalCode)
string:StreetName

[Table:PostalCode]
int:ID
string:CityName

Как я вижу проблему, это всего лишь «короткий список персон» (минимум 1, возможно, до 10 адресов), и мне нужно сравнить этот список адресов с «действительным списком почтовых индексов» для каждого человека.

В надежде на хороший ответ на этот вопрос, так как я застрял уже несколько часов, пытаясь выяснить, какой синтаксис использовать для решения этого более красивого и менее сложного процесса.

Ответы [ 2 ]

4 голосов
/ 21 мая 2009
List<int> peopleIDs = peopleWithRightSkills.Select(p => p.ID).ToList();
List<int> postalIDs = acceptedPostalCodes.Select(c => c.ID).ToList();

var query = db.Persons
  .Where(p => peopleIDs.Contains(p.ID)
  .Where(p => p.Addresses.Any(a => postalIDs.Contains(a.PostalCode_ID))
  );

LinqToSql преобразует каждый элемент в List<int> в параметр. Затем он переведет вызовы метода Contains в предложения TSql IN.

Имейте в виду, что LinqToSql с радостью переведет столько параметров, сколько вы хотите, в параметры (я сам видел 50k), однако SqlServer будет принимать только ~ 2000 параметров. Если ваш список содержит больше элементов, вам нужно разбить эти списки. Если у вас есть 1500 человек с необходимыми навыками и 1000 acceptPostalCodes, вам нужно будет отправить 2500 параметров в SQL Server, что на 400 слишком много, и это даст вам исключение SqlException.

2 голосов
/ 21 мая 2009

Если предположить, что людей много, acceptedPostalCodes не должно быть списком; это должен быть либо отсортированный список / двоичное дерево, либо хеш-таблица, в зависимости от количества кодов. Это само по себе должно быть достаточным для увеличения производительности на порядок. Тогда, да, просто проверьте каждого человека и примите его, если его адрес указан в acceptedPostalCodes.

Нет лучшего способа сделать это, если только у вас нет странных данных (то есть, если одни и те же адреса появляются снова и снова, вы можете кэшировать результаты для этих адресов в некоторой вспомогательной структуре).

Боюсь, я не совсем понимаю, к чему вы стремитесь в остальной части вопроса, представляя структуру таблицы, поэтому я надеюсь, что я не упустил некоторую тонкость того, что вы делаете.

РЕДАКТИРОВАТЬ: так как вы, кажется, заинтересованы в работе с LINQ, вот как вы можете выбрать элементы из peopleWithRightSkills, которые принялиPostalCodes:

var matchedPeople =
peopleWithRightSkills.Where(p => acceptablePostalCodes.Contains(p.Address));
...