如何在LINQ中执行子查询?

时间:2009-01-06 23:27:05

标签: c# linq linq-to-sql

以下是我尝试转换为LINQ的查询示例:

SELECT *
FROM Users
WHERE Users.lastname LIKE '%fra%'
    AND Users.Id IN (
         SELECT UserId 
         FROM CompanyRolesToUsers 
         WHERE CompanyRoleId in (2,3,4) )

CompanyRolesToUsersUsers之间存在FK关系,但它是多对多关系,CompanyRolesToUsers是联结表。

我们已经构建了大部分网站,并且我们已经通过使用PredicateExtensions类构建表达式来完成大部分过滤工作。

直接过滤器的代码如下所示:

 if (!string.IsNullOrEmpty(TextBoxLastName.Text))
 {
     predicateAnd = predicateAnd.And(c => c.LastName.Contains(
                                     TextBoxLastName.Text.Trim()));
 }

e.Result = context.Users.Where(predicateAnd);

我正在尝试在另一个表中为子选择添加谓词。 (CompanyRolesToUsers

我希望能够添加的是:

int[] selectedRoles = GetSelectedRoles();
if( selectedRoles.Length > 0 )
{
    //somehow only select the userid from here ???:
    var subquery = from u in CompanyRolesToUsers
                   where u.RoleID in selectedRoles
                   select u.UserId;

    //somehow transform this into an Expression ???:
    var subExpression = Expression.Invoke(subquery);

    //and add it on to the existing expressions ???:
    predicateAnd = predicateAnd.And(subExpression);
}

有没有办法做到这一点?这很令人沮丧,因为我可以轻松编写存储过程,但我是这个LINQ的新手,我有一个截止日期。我无法找到匹配的例子,但我确定它在某处。

6 个答案:

答案 0 :(得分:76)

这是给你的子查询!

List<int> IdsToFind = new List<int>() {2, 3, 4};

db.Users
.Where(u => SqlMethods.Like(u.LastName, "%fra%"))
.Where(u =>
    db.CompanyRolesToUsers
    .Where(crtu => IdsToFind.Contains(crtu.CompanyRoleId))
    .Select(crtu =>  crtu.UserId)
    .Contains(u.Id)
)

关于这部分问题:

predicateAnd = predicateAnd.And(c => c.LastName.Contains(
                                TextBoxLastName.Text.Trim()));

我强烈建议在创作查询之前从文本框中提取字符串。

string searchString = TextBoxLastName.Text.Trim();
predicateAnd = predicateAnd.And(c => c.LastName.Contains( searchString));

您希望对发送到数据库的内容保持良好的控制。在原始代码中,一个可能的读取是未修剪的字符串被发送到数据库中进行修剪 - 这对数据库来说不是一件好事。

答案 1 :(得分:22)

此语句不需要子查询,最好写成

select u.* 
from Users u, CompanyRolesToUsers c
where u.Id = c.UserId        --join just specified here, perfectly fine
and u.lastname like '%fra%'
and c.CompanyRoleId in (2,3,4)

select u.* 
from Users u inner join CompanyRolesToUsers c
             on u.Id = c.UserId    --explicit "join" statement, no diff from above, just preference
where u.lastname like '%fra%'
  and c.CompanyRoleId in (2,3,4)

话虽如此,在LINQ中它将是

from u in Users
from c in CompanyRolesToUsers 
where u.Id == c.UserId &&
      u.LastName.Contains("fra") &&
      selectedRoles.Contains(c.CompanyRoleId)
select u

from u in Users
join c in CompanyRolesToUsers 
       on u.Id equals c.UserId
where u.LastName.Contains("fra") &&
      selectedRoles.Contains(c.CompanyRoleId)
select u

这又是表达这一点的可敬方式。在这两种情况下,我更喜欢显式的“连接”语法,但它就是......

答案 2 :(得分:5)

这就是我在LINQ中做子查询的方式,我认为这应该得到你想要的。您可以将显式CompanyRoleId == 2 ...替换为您想要的不同角色的其他子查询,也可以将其加入。

from u in Users
join c in (
    from crt in CompanyRolesToUsers
    where CompanyRoleId == 2
    || CompanyRoleId == 3
    || CompanyRoleId == 4) on u.UserId equals c.UserId
where u.lastname.Contains("fra")
select u;

答案 3 :(得分:2)

你可以为你的情况做这样的事情 - (语法可能有点偏离)。另请查看此link

subQuery = (from crtu in CompanyRolesToUsers where crtu.RoleId==2 || crtu.RoleId==3 select crtu.UserId).ToArrayList();

finalQuery = from u in Users where u.LastName.Contains('fra')  && subQuery.Contains(u.Id) select u;

答案 4 :(得分:2)

好的,这是一个获得正确记录的基本连接查询:

   int[] selectedRolesArr = GetSelectedRoles();
    if( selectedRolesArr != null && selectedRolesArr.Length > 0 ) 
    {

    //this join version requires the use of distinct to prevent muliple records
        //being returned for users with more than one company role.
    IQueryable retVal = (from u in context.Users
                        join c in context.CompanyRolesToUsers
                          on u.Id equals c.UserId
                        where u.LastName.Contains( "fra" ) &&
                            selectedRolesArr.Contains( c.CompanyRoleId )
                        select  u).Distinct();
}

但是这里的代码最容易与我们已有的算法集成:

int[] selectedRolesArr = GetSelectedRoles(); 
if ( useAnd ) 
       { 
          predicateAnd = predicateAnd.And( u => (from c in context.CompanyRolesToUsers 
                       where selectedRolesArr.Contains(c.CompanyRoleId) 
                       select c.UserId).Contains(u.Id)); 
        } 
        else 
        { 
           predicateOr = predicateOr.Or( u => (from c in context.CompanyRolesToUsers 
                          where selectedRolesArr.Contains(c.CompanyRoleId) 
                         select c.UserId).Contains(u.Id) ); 
        } 

这要归功于LINQtoSQL forum

上的海报

答案 5 :(得分:1)

这是返回正确记录的SQL版本:

select distinct u.* 
from Users u, CompanyRolesToUsers c
where u.Id = c.UserId        --join just specified here, perfectly fine
and u.firstname like '%amy%'
and c.CompanyRoleId in (2,3,4)

另外,请注意(2,3,4)是从Web应用程序用户的复选框列表中选择的列表,我忘了提到我只是为了简单而硬编码。实际上它是一组CompanyRoleId值,因此它可以是(1)或(2,5)或(1,2,3,4,6,7,99)。

另外我应该更清楚地指出的是,PredicateExtensions用于动态地将谓词子句添加到查询的Where中,具体取决于Web应用程序用户填写的表单字段。所以棘手的部分是我是如何将工作查询转换为LINQ表达式,我可以附加到动态表达式列表。

我将给出一些示例LINQ查询,看看我是否可以将它们与我们的代码集成,然后发布我的结果。谢谢!

烫发