如何在 LINQ 中进行子查询?

下面是我试图转换为 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是一个连接表。

我们已经构建了大部分站点,并且已经通过使用 PredateExtended 类构建 Expressions 来实现大部分过滤。

简单过滤器的代码如下:

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


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

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

我想要补充的是:

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 还是个新手,而且我有一个截止日期。我还没找到一个符合的例子,但我肯定它就在某个地方。

221544 次浏览

这就是我在 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;

此语句不需要子查询,最好将其编写为

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”语法,但是..。

您可以对您的 case-(语法可能有点不对)执行类似的操作。再看看这个 链接

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;

下面是返回正确记录的 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)。

另外,我应该更明确地指定的另一件事情是,根据 web 应用程序用户填写的表单字段,使用述语扩展动态地将谓语子句添加到查询的 Where 中。因此,对我来说棘手的部分是如何将工作查询转换为 LINQ 表达式,我可以附加到表达式的动态列表。

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

Marcel

这里有一个子查询!

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

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

下面是一个获得正确记录的基本连接查询:

   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 论坛的海报