C# 如何在LINQ中执行子查询?
下面是我试图转换为LINQ的查询示例:C# 如何在LINQ中执行子查询?,c#,linq,linq-to-sql,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) ) companyrolestowers和用户之间存在FK关系,但这是一种多对多关系,companyrole
SELECT *
FROM Users
WHERE Users.lastname LIKE '%fra%'
AND Users.Id IN (
SELECT UserId
FROM CompanyRolesToUsers
WHERE CompanyRoleId in (2,3,4) )
companyrolestowers
和用户之间存在FK关系,但这是一种多对多关系,companyrolestowers
是连接表
我们已经构建了大部分网站,并且我们已经通过使用PredicateExtensions类构建表达式来完成大部分过滤工作
直接过滤器的代码如下所示:
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这个东西还不熟悉,我有一个截止日期。我还没有找到一个匹配的示例,但我肯定它在某个地方。这就是我在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)
或
话虽如此,在林克,这将是
from u in Users
from c in CompanyRolesToUsers
where u.Id == c.UserId &&
u.LastName.Contains("fra") &&
selectedRoles.Contains(c.CompanyRoleId)
select u
或
同样,这两种方式都是值得尊敬的。我自己更喜欢在这两种情况下使用显式的“join”语法,但这是…您可以对您的情况执行类似的操作-(语法可能有点不正确)。再看看这个
以下是返回正确记录的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)
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();
}
另外,请注意,(2,3,4)是由web应用程序用户从复选框列表中选择的列表,我忘了提到,为了简单起见,我只是硬编码了它。实际上它是一个CompanyRoleId值数组,所以它可以是(1)或(2,5)或(1,2,3,4,6,7,99)
我还应该更清楚地说明的另一件事是,谓词扩展用于动态地将谓词子句添加到查询的Where,具体取决于web应用程序用户填写的表单字段。因此,对我来说,棘手的部分是如何将工作查询转换为一个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)
)
我强烈建议在编写查询之前从文本框中提取字符串
string searchString = TextBoxLastName.Text.Trim();
predicateAnd = predicateAnd.And(c => c.LastName.Contains( searchString));
您希望对发送到数据库的内容保持良好的控制。在原始代码中,一种可能的读取方式是将未修剪的字符串发送到数据库中进行修剪,这对于数据库来说不是很好的工作。好的,下面是一个基本的连接查询,可以获取正确的记录:
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)
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) );
}
多亏了上的海报,这是一个非常好的答案,但所有这些方法的行为都不同,通常会产生非常不同的sql,sql的性能可能好也可能坏,通常使用直接连接会产生最好的sql,但并非总是……根据我对相当复杂的查询的经验,编写linq查询的方式会有所不同sql server 2005有时无法为具有深层子查询嵌套的查询生成最佳查询计划。。。我不得不多次优化一些复杂的linq查询……嗯,既然可以“连接和区分”,为什么还要“过滤”呢。是不是因为“加入和区分”是自然而令人兴奋的sql,而“筛选”是不够令人兴奋的?好吧,既然我被邀请了。。。事实:没有一个优化器可以将一个Distinct(n^2)转换成一个filter(n)。99%的情况下,使用“Distinct”编写的查询是错误的。当你想加入一个表进行过滤,但不想在加入的左侧有重复的记录时,可以使用半加入(即子查询)!但我确信LINQ无论如何都会将其更改为相同的SQL…我必须在“==4”之后加上“select crt”以避免错误“查询体必须以select子句或group子句结尾”@Noah我不太确定。根据我的经验,LINQ查询的性能因您编写查询的方式而有很大的不同(正如任何SQL查询所期望的那样)。我只是想表明他想这样做……而不是强制执行我自己的查询opinion@TheSoftwareJedi我看到你在这里对多个回复发了这条评论,但请记住,在linq中搜索帮助进行子查询的人很可能会找到此线程,他们的情况可能与OPs不完全相同。感谢您提供的有用代码。这当然表明了事物是如何被联系在一起的。在将字符串操作交给Linq之前执行该操作可能也是正确的,将它们分开当然不会有什么坏处。
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) );
}