如何在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是交接表。

我们已经build立了大部分网站,而且我们已经使用PredicateExtensions类构buildexpression式,从而完成大部分的过滤工作。

简单的filter的代码看起来像这样:

  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的事情,我有一个截止date。 我没有find匹配的例子,但我确定它在某处。

这是你的子查询!

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

我强烈build议在创build查询之前从文本框中提取string。

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

您希望对发送到数据库的内容保持良好的控制。 在原始代码中,一个可能的读法是将一个未修改的string发送到数据库中进行修剪 – 这对于数据库来说不是一件好事。

这个语句没有需要的子查询,写得更好

 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 

再次,这两个都是可敬的方式来表示这一点。 在这两种情况下,我更喜欢明确的“连接”语法,但是在那里它是…

这是我一直在LINQ中进行子查询,我认为这应该得到你想要的。 您可以将显式的CompanyRoleId == 2 …replace为您想要的不同angular色的另一个子查询,也可以将其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; 

你可以为你的情况做这样的事情 – (语法可能有点closures)。 也看看这个链接

 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; 

好的,这是一个基本的连接查询,获取正确的logging:

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

但是,下面是最容易与我们已有的algorithm集成的代码:

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

以下是返回正确logging的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应用程序用户从checkbox列表中select的一个列表,我忘了提及我为了简单而对其进行了硬编码。 真的,这是一系列的CompanyRoleId值,所以它可能是(1)或(2,5)或(1,2,3,4,6,7,99)。

此外,我应该更清楚地指出的另一件事是,PredicateExtensions用于dynamic地添加谓词子句到查询的位置,这取决于Web应用程序用户填写的表单字段。所以对我来说棘手的部分是如何将工作查询转换为可附加到dynamicexpression式列表的LINQexpression式。

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

烫发