【问题标题】:Tricky LINQ Query using Entity Framework使用实体框架的棘手 LINQ 查询
【发布时间】:2014-03-12 10:02:23
【问题描述】:

我一直在研究一些 LINQ,但我似乎无法弄清楚如何过滤这些数据。我对 LINQ 还是很陌生,这是我遇到的第一个真正的绊脚石。

我有桌子:

任务ID |姓名 |类型ID |父任务ID

价值观:

1 |任务名称 | 1 |空

2 |任务名称| 6 |空

3 |任务名称| 6 | 4

4 |任务名称| 2 |空

5 |任务名称| 6 | 4

6 |任务名称| 3 | 4

7 |任务名称| 6 | 1

8 |任务名称| 6 | 1

9 |任务名称| 2 |空

子任务是具有 ParentTaskID 的任务。

我需要获得所有符合此条件的任务:

  • 如果 Task 没有分配的 SubTasks 且 TypeID = 6,则删除 Task
  • 如果 ParentTask 的 SubTasks ALL 的 TypeID = 6,则删除 Parent 和 Subtasks

因此我希望能回来

3 |任务名称| 6 | 4

4 |任务名称| 2 |空

5 |任务名称| 6 | 4

6 |任务名称| 3 | 4

9 |任务名称| 3 |空

上表结果如下:

TaskID 1 已删除,因为 TaskID 7 和 8 是 TypeID = 6 的 SubTasks

TaskID 2 被移除,因为它没有 SubTasks 并且 TypeID = 6

TaskID 7 和 8 已删除,因为它们是父任务 1 的 TypeID = 6 的子任务

甚至可以在 LINQ 中执行此操作,还是我必须在 foreach 循环中执行此操作?

我的解决方案

所以我最终使用 Foreach 以老式方式执行此操作,并让 Resharper 将它可以转换回 LINQ 的内容。

        var parents = tasks.Where(task => task.SubTasks.Any());
        var tasksWithNoSubs = tasks.Where(task => !task.SubTasks.Any());
        var parentIdsToRemove = new List<int?>();
        var childIdsToRemove = new List<int?>();

        foreach (var parent in parents)
        {
            var children = tasks.Where(task => task.ParentTaskID == parent.Id);
            if (children.All(task => task.StatusId == 6))
            {
                parentIdsToRemove.Add(parent.Id); //mark this parent for removal
                childIdsToRemove.AddRange(Enumerable.Select(children.Select(child => child.Id), dummy => (int?) dummy));
            }
        }

        //get all Tasks that have NO Subtasks and also have a StatusID = 6
        parentIdsToRemove.AddRange(Enumerable.Select((from task in tasksWithNoSubs where task.StatusId == 6 select task.Id), dummy => (int?) dummy));

        //get a list of children with an Id contained in the list parentIdsToRemove
        var tasksToRemove =
            tasks.Where(task => parentIdsToRemove.Contains(task.Id) || childIdsToRemove.Contains(task.Id));

        //remove the tasks from the collection
        tasks = tasks.Where(task => !tasksToRemove.Contains(task));

【问题讨论】:

  • 不应该删除 2 和 3,因为 2 有一个类型为 6 的子任务吗? (而不是因为没有子任务而只删除了 2 个)?不应该删除 9,因为它没有 id 为 6 的子任务(实际上根本没有子任务)?等等,对于最后一个,第一个标准是否应该被声明为“删除 TypeID = 6 且没有子任务的任务”?我把它读作 typeid=6... 的子任务
  • 为了实际解决这个问题,我想先加入一些组合,然后按父任务分组,然后为可以查看分组子任务的两个标准中的每一个添加一个 where 子句。我不确定我是否可以自己编写查询,但希望这会给您一个开始的想法。
  • 嘿克里斯,很可能我有一些结果不正确,所以请原谅我。我现在更新了 OP,给 3 一个 4 的父母,希望这看起来是正确的。
  • 9 不应该被移除,因为它没有 Sub Tasks 并且没有 TypeID = 6
  • 您需要在实际问题中阐明您的第一个项目符号。是说“删除 TypeID 为 6 且没有子任务的任务”还是“删除没有 TypeID = 6 的子任务的任务”..? (注意,这就是为什么我们应该放弃自然语言而所有人都说 C++。Err,对不起,C# - 忘了我在哪里!)

标签: c# sql .net linq entity-framework


【解决方案1】:

您应该直接在您的任务对象上拥有属性(由实体框架生成,其中包括对父/子的引用,我假设父对象名为 ParentTask 而子对象在我的示例中被命名为 Tasks,请相应地重命名。你不要'不想使用 ID,这很不符合 EF ish,EF 和 linq 的全部意义在于让它处理所有 DB 非规范化废话并使用对象图。

YourContext.Tasks
    // Only keep those where it isn't true that it contains no tasks while being type 6
    .Where(t=>!((!t.Tasks.Any()) && t.TypeId == 6))
    // Remove all parents that contains subtasks and only have subtasks with type 6
    .Where(t=>!(t.Tasks.Any() && t.Tasks.All(sub=>sub.TypeId == 6)))
    // Same process on the children, if it has a parent and that parent matches the previous filter
    .Where(t=>!(t.ParentTask != null &&(t.ParentTask.Tasks.Any() && t.ParentTask.Tasks.All(sub=>sub.TypeId == 6))))

请注意,这是完全未经测试的,如果它不起作用,请提供指向您已编译模型 DLL 的链接

【讨论】:

  • 我的实体名为 Task,其中包含一个 Id、TypeId 和 ParentTaskId 属性(以及许多其他属性)
【解决方案2】:

查看原帖中的解决方案(也发布在此处)。

    var parents = tasks.Where(task => task.SubTasks.Any());
    var tasksWithNoSubs = tasks.Where(task => !task.SubTasks.Any());
    var parentIdsToRemove = new List<int?>();
    var childIdsToRemove = new List<int?>();

    foreach (var parent in parents)
    {
        var children = tasks.Where(task => task.ParentTaskID == parent.Id);
        if (children.All(task => task.StatusId == 6))
        {
            parentIdsToRemove.Add(parent.Id); //mark this parent for removal
            childIdsToRemove.AddRange(Enumerable.Select(children.Select(child => child.Id), dummy => (int?) dummy));
        }
    }

    //get all Tasks that have NO Subtasks and also have a StatusID = 6
    parentIdsToRemove.AddRange(Enumerable.Select((from task in tasksWithNoSubs where task.StatusId == 6 select task.Id), dummy => (int?) dummy));

    //get a list of children with an Id contained in the list parentIdsToRemove
    var tasksToRemove =
        tasks.Where(task => parentIdsToRemove.Contains(task.Id) || childIdsToRemove.Contains(task.Id));

    //remove the tasks from the collection
    tasks = tasks.Where(task => !tasksToRemove.Contains(task));

感谢大家的贡献,def学到了更多关于LINQ的知识。

【讨论】:

    【解决方案3】:

    第二次非常重要的编辑

    对!所以,在很长一段时间和一些误解之后第三次幸运 - 这是一个声明:

    entities.
    
    // discard tasks of type 6 and with no subtasks
    Where(x => !(x.TypeId == 6 && x.ParentTaskId == null && 
          !entities.Any(y => y.ParentTaskId == x.TaskId))).
    
    // discard tasks with subtasks where all those subtasks are of type 6
    Where(x => !(entities.Any(y => y.ParentTaskId == x.TaskId) &&
                 entities.Count(y => y.ParentTaskId == x.TaskId) ==
                 entities.Count(y => y.ParentTaskId == x.TaskId && y.TypeId == 6))).
    
    // discard subtasks that have parents that are not to be found in the list of 
    // tasks that were not discarded for having subtasks that were all of type 6.
    Where(z => z.ParentTaskId == null || 
                 entities.Where(x => !(entities.Any(y => y.ParentTaskId == x.TaskId) &&
                                       entities.Count(y => y.ParentTaskId == x.TaskId) ==
                                       entities.Count(y => y.ParentTaskId == x.TaskId && y.TypeId == 6))).
                                                Any(y => y.TaskId == z.ParentTaskId));
    

    【讨论】:

    • 这在 EF 中没有做任何事情。这些都不是在 DB 端完成的,它应该是。
    • 我离解决方案越来越近了,我完成后会发布。
    • @nz_dev 试一试,我要躺下。
    • @user1158692 我试过这个,但是返回的数据有问题,不得不恢复到我在原始帖子中发布的另一个解决方案。
    猜你喜欢
    • 2021-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-19
    • 1970-01-01
    • 2012-09-08
    相关资源
    最近更新 更多