Есть ли лучший способ написать этот запрос LINQ Франкенштейна, который ищет значения в дочерней таблице и упорядочивает их по релевантности? - PullRequest
2 голосов
/ 11 марта 2010

У меня есть таблица Users и таблица UserSkills один ко многим. Мне нужно иметь возможность искать пользователей на основе навыков. Этот запрос берет список желаемых навыков и ищет пользователей, которые имеют эти навыки. Я хочу отсортировать пользователей по количеству желаемых навыков, которыми они обладают. Таким образом, если у пользователя есть только 1 из 3 желаемых навыков, он будет дальше в списке, чем пользователь, имеющий 3 из 3 желаемых навыков.

Я начинаю с моего списка идентификаторов умений, разделенных запятыми, которые ищутся:

List<short> searchedSkillsRaw = skills.Value.Split(',').Select(i => short.Parse(i)).ToList();

Затем я отфильтрую только тех пользователей, которые доступны для поиска:

List<User> users = (from u in db.Users
                    where
                        u.Verified == true &&
                        u.Level > 0 &&
                        u.Type == 1 &&
                        (u.UserDetail.City == city.SelectedValue || u.UserDetail.City == null)
                    select u).ToList();

и затем приходит сумасшедшая часть:

var fUsers = from u in users
             select new
             {
                 u.Id,
                 u.FirstName,
                 u.LastName,
                 u.UserName,
                 UserPhone = u.UserDetail.Phone,
                 UserSkills = (from uskills in u.UserSkills
                               join skillsJoin in configSkills on uskills.SkillId equals skillsJoin.ValueIdInt into tempSkills
                               from skillsJoin in tempSkills.DefaultIfEmpty()
                               where uskills.UserId == u.Id
                               select new
                               {
                                   SkillId = uskills.SkillId,
                                   SkillName = skillsJoin.Name,
                                   SkillNameFound = searchedSkillsRaw.Contains(uskills.SkillId)
                               }),
                 UserSkillsFound = (from uskills in u.UserSkills
                                    where uskills.UserId == u.Id && searchedSkillsRaw.Contains(uskills.SkillId)
                                    select uskills.UserId).Count()
             } into userResults
             where userResults.UserSkillsFound > 0
             orderby userResults.UserSkillsFound descending
             select userResults;

и это работает! Но это кажется супер раздутым и неэффективным для меня. Особенно вторичная часть, которая подсчитывает количество найденных умений.

Спасибо за любой совет, который вы можете дать.

- г * * 1016

Ответы [ 2 ]

3 голосов
/ 12 марта 2010

Я думаю, что должно сработать:

(from u in users
where u.UserSkills.Any(skill => searchedSkillsRaw.Contains(skill.SkillId))
select new
{
    u.Id,
    u.FirstName,
    u.LastName,
    u.UserName,
    UserPhone = u.UserDetail.Phone,
    UserSkills = u.UserSkills,
    UserSkillsFound = u.UserSkills.Where(skill => searchedSkillsRaw.Contains(skill.SkillId)).Count()
} into userResults
orderby userResults.UserSkillsFound descending
select userResult).ToList();

Однако, поскольку этот запрос выполняется на сервере SQL, я настоятельно рекомендую удалить вызов ToList () из первого запроса. Потому что это на самом деле заставляет LINQ выполнять два отдельных запроса на сервере SQL. Вместо этого вы должны изменить его на IQueryable. Сила LINQ состоит в том, чтобы создавать запросы в несколько этапов без необходимости выполнять их между ними. Таким образом, ToList должен вызываться только в конце, когда весь запрос был построен. Фактически, то, что вы в настоящее время делаете, выполняет второй запрос в памяти, а не на сервере базы данных.

Что касается отношения «один ко многим» для UserSkills, вам не нужно делать соединение Explicity в LINQ. Вместо этого вы можете просто получить доступ к свойству коллекции.

Дайте мне знать, если вам нужно больше объяснений.

Michael

0 голосов
/ 11 марта 2010

Почему бы просто не позволить людям сделать, скажем, fUsers.UserSkills.Count()? Это уменьшит объем данных, полученных с сервера.

В качестве альтернативы, вы можете создать представление, в котором есть вычисляемое поле, а затем сопоставить его с типом. Выдвинет запрос на обратный отсчет в БД.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...