Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在LINQ中执行子查询?_C#_Linq_Linq To Sql - Fatal编程技术网

C# 如何在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

下面是我试图转换为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关系,但这是一种多对多关系,
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) ); 
        }