【问题标题】:LINQ Conditionally Add JoinLINQ 有条件地添加连接
【发布时间】:2014-05-19 17:58:33
【问题描述】:

我有一个 LINQ 查询,我试图从 2 个表中返回数据,但我加入的表是有条件的。

这是我想做的:

if (teamType == "A"){
    var query = from foo in context.People
                join foo2 in context.PeopleExtendedInfoA
                select foo;
}
else {
    var query = from foo in context.People
                join foo2 in context.PeopleExtendedInfoB
                select foo;
}

稍后我将进一步过滤查询。我显然无法以这种方式设置它,因为我将无法访问 if 块之外的“查询”,但它显示了我正在尝试做的事情。这是我稍后尝试对查询执行的操作的示例:

if (state != null)
{
     query = query.Where(p => p.State == state);
}

if (query != null) {
   var queryFinal = from foo in query
         select new PeopleGrid()
         {
              Name = foo.Name,
              Address = foo.Address,
              Hobby = foo2.Hobby
         }
}

我要返回的是表 foo 中的所有数据,然后是连接表中的一个字段,但根据逻辑,连接表会有所不同。 PeopleExtendedInfoA 和 PeopleExtendedInfoB 都具有列“爱好”,但我无法从连接表中访问“爱好”,这是我需要从连接表中获取的唯一字段。

我该怎么做呢?

【问题讨论】:

    标签: c# linq join linq-to-entities


    【解决方案1】:

    PeopleExtendedInfoAPeopleExtendedInfoB 是否继承自同一个基类?您可以创建一个 IQueryable<BaseClass> 并让 linq 提供程序在您添加连接时为您解决它。示例:

    IQueryable<BasePeople> basePeople;
    if (teamType == "A")
       basePeople = context.PeopleExtendedInfoA;
    else
       basePeople = context.PeopleExtendedInfoB;
    
    var query = from foo in context.People
                join foo2 in basePeople on foo.Id equals foo2.PeopleId
                select new PeopleGrid()
                {
                  Name = foo.Name,
                  Address = foo.Address,
                  Hobby = foo2.Hobby
                };
    

    【讨论】:

    • OP 没有指定这是否是 Linq-to-Objects/SQL/Entities,但我认为这种类型的查询在某些支持 db 的 Linq 提供程序中不起作用(至少是旧版本,不确定新的)。
    • 不,PeopleExtendedInfoA 和 PeopleExtendedInfoB 是独立的类,实际上是去规范化的。 People 类中的记录在 PeopleExtendedInfoA 或 PeopleExtendedInfoB 中有 1 个匹配记录,具体取决于他们的“团队”。我需要摆脱 PeopleExtendedInfoA 和/或 PeopleExtendedInfoB 只是一个名为“爱好”的字段。抱歉,如果设计在我的示例中没有意义,我保证它在我的实际应用程序中是有意义的。
    • @Ocelot20 许多查询提供程序实际上确实支持实体继承并允许查询是协变的。您需要做一些工作来设置继承,但至少有可能支持它。
    • 抱歉没有澄清。它是 Linq to Entities。
    • @Servy:是的,但上次我使用 EF 继承时,它可以很好地执行的查询类型肯定是有限的(我听说它已经改进了,不确定有多少)。想记下它,因为Linq-to-SQL 绝对不会支持它。那么@user3653675,它目前在您的数据库中的结构如何?
    【解决方案2】:

    试试这样:

     var queryFinal = from foo in query
                      where foo.State == state !=null ? state : foo.State
             select new PeopleGrid()
             {
                  Name = foo.Name,
                  Address = foo.Address,
                  Hobby = foo2.Hobby
             }
    

    【讨论】:

      【解决方案3】:

      您可以查询包含相关字段的中间类型,或者如果查询足够简单,您可以使用匿名类型,如下所示。重要的部分是 Join 调用都具有相同的返回类型({ p.Name, a.Hobby }{ p.Name, b.Hobby } 都将是相同的匿名类型)。

      var baseQuery = context.People;
      
      var joined = type == "A" ?
          baseQuery.Join(PeopleExtendedInfoA,
              p => p.Id,
              a => a.PeopleId,
              (p, a) => new { p, a.Hobby }) :
          baseQuery.Join(PeopleExtendedInfoB,
              p => p.Id,
              b => b.PeopleId,
              (p, b) => new { p, b.Hobby });
      
      var result = joined.Select(x => new
          {
              x.p.Name,
              x.p.Address,
              // etc.
              x.Hobby
          };
      

      【讨论】:

      • 谢谢。这个想法似乎效果最好,特别是因为我只需要 PeopleExtendedInfoA 或 PeopleExtendedInfoB 中的一个字段,并且它们的类型/名称相同。有没有什么方法可以不用声明所有字段? “人员”需要返回的字段很多,所以我试图减少重复/不必要的代码。例如,我可以执行以下操作: (p, a) => new { p, a.Hobby }) 其中 p 是该表中的所有字段,并且仅从表 a 返回 Hobby?
      • 是的,你当然可以这样做(见我上面的更新)。尽管为了更清楚起见,我建议命名比我的示例更好一些。
      • 谢谢。我最终做了类似的事情,但我只是加入了结果查询,这样我仍然可以对 baseQuery 进行正常过滤,而不必担心编译器会抱怨不同的类型。
      • 不确定我是否关注。此解决方案不会限制您对 baseQuery 进行过滤,它只是 Join 部分的示例。
      【解决方案4】:

      我想通了。感谢大家的所有回复,它让我的大脑再次工作并给了我一些新想法(即使我没有直接接受任何一个)我意识到我需要在最后而不是开始时那样做加入我不必处理不同类型的过滤。这就是我所做的:

      var query = from foo in context.People
                  select foo;
      
      if (state != null)
      {
          query = query.Where(p => p.State == state);
      }
      
      if (query != null) {
         if (teamType == "A")
         {
            var queryFinal = from foo in query
                             join foo2 in context.PeopleExtendedInfoA
            select new PeopleGrid()
            {
                Name = foo.Name,
                Address = foo.Address,
                Hobby = foo2.Hobby
            }
         }
         else
         {
            var queryFinal = from foo in query
                             join foo2 in context.PeopleExtendedInfoB
            select new PeopleGrid()
            {
                Name = foo.Name,
                Address = foo.Address,
                Hobby = foo2.Hobby
            }
         }
      }
      

      【讨论】:

      • 这仍然受到您原来的问题所述的问题的困扰,即queryFinal 不能在 if/else 块之外使用。你是在这两种情况下都立即返回queryFinal 吗?此外,query 本质上不能为空,因此不需要进行空检查。
      • 是的,PeopleGrid 将在此代码结束后立即返回。很抱歉没有说清楚。有一堆 if/else 块,但我把它们放在 queryFinal 之前。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-02
      • 1970-01-01
      • 2012-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多