【问题标题】:Conditional Join with LINQ使用 LINQ 进行条件连接
【发布时间】:2013-01-21 17:36:43
【问题描述】:

我想创建一个等效于Left Join 的 LINQ 连接语句

我的桌子是这样设置的:

Recipe
    RecipeID
    ...

Instruction
    RecipeID
    StepID
    SomeFlag
    ...

等效 SQL:

SELECT *
FROM Recipe r
LEFT JOIN Instruction i
    ON r.RecipeID = i.RecipeID
    AND SomeFlag > 0

这是我目前所拥有的:

var tmp = db.Recipe
    .GroupJoin(
        db.Instruction,
        r => r.RecipeID,
        i => i.RecipeID,
        (r, i) => new {r, i},
        ???);

首先,GroupJoin 是此类操作的正确选择吗?据我了解,Join 相当于 SQL 的“Inner Join”,GroupJoin 相当于“Left Join”。其次,获得我想要的结果的正确语法是什么?我已经搜索了一段时间,但似乎找不到合适的答案使用扩展方法

【问题讨论】:

  • 查看stackoverflow.com/questions/1092562/left-join-in-linq?rq=1 了解如何进行左连接。要应用 SomeFlag > 0,您可以在加入前使用 where 条件
  • 您要检索什么?有许多不同的方法可以检索相同的数据。
  • @gabba 我想我可以用斜体或粗体表示,但我专门寻找使用extention methods 的解决方案。下面塞德里克的回答告诉我在加入之前过滤的能力,我想这可以从你提供的链接中推断出来。
  • @Bob。提供的 SQL 不清楚吗?我希望加入RecipeInstruction 等效RecipeIDSomeFlag > 0

标签: c# linq


【解决方案1】:

别忘了阅读来自 (GroupJoin: MSDN http://msdn.microsoft.com/en-us/library/bb535047.aspxJoin MSDN http://msdn.microsoft.com/fr-fr/library/bb534675.aspx) 的帮助

GroupJoinJoin 的最后一个参数是可选的(通过重载),通常不使用。 这是一个允许您指定如何比较r.RecipeIDi.RecipeID 的函数。由于RecipeID 必须是整数,所以使用默认比较器是一个不错的选择。那就顺其自然吧:

var tmp = db.Recipe
    .Join(db.Instruction,
          r => r.RecipeID,
          i => i.RecipeID,
          (r, i) => new {r, i});

现在您想要删除所有具有SomeFlag > 0 的指令。为什么在加入之前不这样做呢? 像这样:

var tmp = db.Recipe
    .Join(db.Instruction.Where(instruction => instruction.SomeFlag > 0),
          r => r.RecipeID,
          i => i.RecipeID,
          (r, i) => new {r, i});

更新

@usr 完美地评论说 Join 执行 INNER JOIN。

您可能已经说过,LINQ 没有用于 INNER、OUTER、LEFT、RIGHT 连接的不同方法。要了解特定 SQL 连接的等效 LINQ,您可以在 MSDN (http://msdn.microsoft.com/en-us/library/vstudio/bb397676.aspx) 上找到帮助。

var tmp = from recipe in Recipes
          join instruction in
              from instruction in Instructions
              where instruction.SomeFlag > 0
              select instruction
          on recipe.RecipeID equals instruction.RecipeID into gj
          from instruction in gj.DefaultIfEmpty()
          select new
          {
              recipe,
              instruction
          };

使用扩展方法有点丑陋的解决方案:

var tmp = Recipes.GroupJoin(Instructions.Where(instruction => instruction.SomeFlag > 0),
                            recipe => recipe.RecipeID,
                            instruction => instruction.RecipeID,
                            (recipe, gj) => new { recipe, gj })
                 .SelectMany(@t => @t.gj.DefaultIfEmpty(), 
                             (@t, instruction) => new
                             {
                                 @t.recipe,
                                 instruction
                             });

【讨论】:

  • 联接始终是内部联接。
  • @CédricBignon 这很有趣。我不知道在加入之前可以过滤右手表格。只是为了好玩,您将如何在条件连接中利用配方表中的值?例如,如果我们将SomeFlag 添加到Recipe 表中,您将如何在Recipe.SomeFlag == Instruction.SomeFlag 上编写条件连接?
  • 这里你必须使用 GroupJoin 和 Join 方法的最后一个参数。为了回答之前的评论,我将修改我的帖子以考虑到 LEFT OUTER JOIN。
【解决方案2】:

如果我不理解你,请告诉我,但是这个扩展方法返回的结果与你在 sql 中获得的结果相同。

public static IEnumerable<ResultType> GetLeftJoinWith(this IEnumerable<Recipe>, IEnumerable<Instructions> ins)
{
    var filteredInstructions = ins.Where(x => x.SomeFlag > 0);

    var res = from r in rec
              join tmpIns in filteredInstructions on r.RecipeID equals t.RecipeID into instructions
              from instruction in instructions.DefaultIfEmpty()
              select new { r, instruction };

   return res;
}

【讨论】:

  • 聪明的解决方案 - 但我不认为这是它自己的扩展方法的好候选。我认为如果将其更改为在现有方法中使用Func&lt;T,...&gt;,那将是一个更清洁的解决方案。我真的很惊讶 MS 没有在 linq 中添加更直接的多列左连接方式。
  • James,你说得对,但我只是想给出一个想法,而不是提出明确的解决方案。
  • 我不想创建扩展方法。我只是说我正在寻找一种使用扩展方法语法而不是 LINQ 语法的解决方案。 This 链接将清楚地显示两种语法之间的差异。我非常感谢您为实际创建扩展方法所付出的额外努力。
【解决方案3】:

试试这个

var model = db.Recipe
            .GroupJoin(db.Instructions.Where(instruction => instruction.SomeFlag > 0),r => r.RecipeID,i => i.RecipeID, (r, i) => new { Recipe = r, Instructions = i })
            .SelectMany(t => t.Instructions.DefaultIfEmpty(),(t, Instructions) => new
            {
                 Recipe = t.Recipe,
                 Instructions = Instructions
            });

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-01
    • 2016-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多