有没有更好的方法来编写这个frankenstein LINQ查询,在查询子表中的值并按相关性对它们进行排序?

时间:2010-03-11 19:45:00

标签: c# asp.net linq linq-to-sql

我有一个User表和一个到多个UserSkills表。我需要能够根据技能搜索用户。此查询会获取所需技能的列表,并搜索具有这些技能的用户。我想根据用户所需技能的数量对用户进行排序。因此,如果用户只有3个所需技能中的1个,那么他将比列出3个所需技能中的3个的用户更进一步。

我从以逗号分隔的正在搜索的技能ID列表开始:

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;

这有效!但对我来说,它似乎超级膨胀,效率低下。特别是次要部分,计算发现的技能数量。

感谢您提供任何建议。

- R的

2 个答案:

答案 0 :(得分:3)

我认为应该这样做:

(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一对多关系,您不需要在LINQ中进行明确的连接。您只需访问集合属性即可。

如果您需要更多解释,请告诉我。

迈克尔

答案 1 :(得分:0)

为什么不让人们这样做,比如fUsers.UserSkills.Count()?它将首先减少从服务器检索的数据量。

或者,您可以创建一个包含计算字段的视图,然后将其映射到某个类型。将查询倒计数到数据库。