English 中文(简体)
如何在LINQ中执行子查询?
原标题:
  • 时间:2009-01-06 23:27:05
  •  标签:

这是我试图转换为LINQ的查询示例:

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

CompanyRolesToUsersUsers之间存在外键关系,但是它们以多对多关系相连,并且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,而且我有一个截止日期。我找不到匹配的示例,但我肯定它在某个地方。

最佳回答

这里有一个子查询给你!

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));

您希望对发送到数据库的内容进行良好的控制。在原始代码中,可能的一种读取方式是将未修剪的字符串发送到数据库以进行修剪,这不是数据库应该做的好工作。

问题回答

这个语句不需要子查询,最好这样写。

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

再次强调,这两种表示方法都是可敬的。我个人更喜欢在两种情况下使用明确的“join”语法,但是这就是我的看法...

这是我在LINQ中执行子查询的方式,我认为这应该能得到你想要的结果。你可以用不同的子查询替换明确的CompanyRoleId == 2... 来获取不同的角色,或者将其加入到JOIN语句中。

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;

你可以为你的案例做类似这样的事情 - (语法可能有点问题)。还要看看这个链接

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;

好的,这里是一个基本的联接查询,可以获取正确的记录:

   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论坛上的一个海报。

这是一个返回正确记录的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 用于根据 Web 应用程序用户填写的哪些表单字段,在查询中动态添加谓词子句到 Where 子句。因此,对我来说,棘手的部分是如何将工作查询转换为我可以附加到动态表达式列表的 LINQ 表达式。

我将尝试一些样本LINQ查询,看看我能否将它们与我们的代码集成,然后发布我的结果。谢谢!

马塞尔





相关问题
热门标签