web-dev-qa-db-ja.com

LINQクエリの問題:各目標から最初のタスクを選択する

クエリの作成方法に関する提案を探しています。 Goalごとに、Task.Sequenceを含むタスクに加えて、最初のTaskShowAlways == trueでソート)を選択します。 (実際のクエリはより複雑ですが、このクエリは私が実行している制限を示しています。)

私はこのようなものを試しました:

var tasks = (from a in DbContext.Areas
             from g in a.Goals
             from t in g.Tasks
             let nextTaskId = g.Tasks.OrderBy(tt => tt.Sequence).Select(tt => tt.Id).DefaultIfEmpty(-1).FirstOrDefault()
             where t.ShowAlways || t.Id == nextTaskId
             select new CalendarTask
             {

                 // Member assignment

             }).ToList();

しかし、このクエリは複雑すぎるようです。

System.InvalidOperationException: 'Processing of the LINQ expression 'OrderBy<Task, int>(
    source: MaterializeCollectionNavigation(Navigation: Goal.Tasks(< Tasks > k__BackingField, DbSet<Task>) Collection ToDependent Task Inverse: Goal, Where<Task>(
        source: NavigationExpansionExpression
            Source: Where<Task>(
                source: DbSet<Task>,
                predicate: (t0) => Property<Nullable<int>>((Unhandled parameter: ti0).Outer.Inner, "Id") == Property<Nullable<int>>(t0, "GoalId"))
            PendingSelector: (t0) => NavigationTreeExpression
                Value: EntityReferenceTask
                Expression: t0
        ,
        predicate: (i) => Property<Nullable<int>>(NavigationTreeExpression
            Value: EntityReferenceGoal
            Expression: (Unhandled parameter: ti0).Outer.Inner, "Id") == Property<Nullable<int>>(i, "GoalId"))), 
    keySelector: (tt) => tt.Sequence)' by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core. See https://go.Microsoft.com/fwlink/?linkid=2101433 for more detailed information.'

問題はlet nextTaskId =...という行です。コメントアウトしてもエラーはありません。 (しかし、私は自分が求めているものを取得できません。)

エラーメッセージの詳細がわからないことはすぐに認めます。これに取り組むために私が考えることができる他の唯一の方法については、すべてのTasksを返し、クライアントでそれらをソートしてフィルタリングします。しかし、私の好みは、不要なデータを取得することではありません。

誰でもこのクエリにアプローチする他の方法を見ることができますか?

注:私はVisual Studioと.NETの最新バージョンを使用しています

更新:

このクエリに対して別の、しかし効率の悪いアプローチを試しました。

var tasks = (DbContext.Areas
      .Where(a => a.UserId == UserManager.GetUserId(User) && !a.OnHold)
      .SelectMany(a => a.Goals)
      .Where(g => !g.OnHold)
      .Select(g => g.Tasks.Where(tt => !tt.OnHold && !tt.Completed).OrderBy(tt => tt.Sequence).FirstOrDefault()))
    .Union(DbContext.Areas
      .Where(a => a.UserId == UserManager.GetUserId(User) && !a.OnHold)
      .SelectMany(a => a.Goals)
      .Where(g => !g.OnHold)
      .Select(g => g.Tasks.Where(tt => !tt.OnHold && !tt.Completed && (tt.DueDate.HasValue || tt.AlwaysShow)).OrderBy(tt => tt.Sequence).FirstOrDefault()))
    .Distinct()
    .Select(t => new CalendarTask
    {
        Id = t.Id,
        Title = t.Title,
        Goal = t.Goal.Title,
        CssClass = t.Goal.Area.CssClass,
        DueDate = t.DueDate,
        Completed = t.Completed
    });

しかし、これもエラーを引き起こしました:

System.InvalidOperationException: 'Processing of the LINQ expression 'Where<Task>(
    source: MaterializeCollectionNavigation(Navigation: Goal.Tasks (<Tasks>k__BackingField, DbSet<Task>) Collection ToDependent Task Inverse: Goal, Where<Task>(
        source: NavigationExpansionExpression
            Source: Where<Task>(
                source: DbSet<Task>, 
                predicate: (t) => Property<Nullable<int>>((Unhandled parameter: ti).Inner, "Id") == Property<Nullable<int>>(t, "GoalId"))
            PendingSelector: (t) => NavigationTreeExpression
                Value: EntityReferenceTask
                Expression: t
        , 
        predicate: (i) => Property<Nullable<int>>(NavigationTreeExpression
            Value: EntityReferenceGoal
            Expression: (Unhandled parameter: ti).Inner, "Id") == Property<Nullable<int>>(i, "GoalId"))), 
    predicate: (tt) => !(tt.OnHold) && !(tt.Completed))' by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core. See https://go.Microsoft.com/fwlink/?linkid=2101433 for more detailed information.'
7
Jonathan Wood

クエリを2つのステップに分けると思います。最初に、各目標をクエリし、最小シーケンスタスクを取得して格納します(おそらく{NextTaskId、Goal}のような匿名タイプを使用)。次に、一時データをクエリして結果を取得します。例えば

Areas.SelectMany(x=>x.Goals)
    .Select(g=>new {
        NextTaskId=g.Tasks.OrderBy(t=>t.Sequence).FirstOrDefault()?.Id,
        Tasks=g.Tasks.Where(t=>t.ShowAlways)
      })
    .SelectMany(a=>a.Tasks,(a,task)=>new {
            NextTaskId = a.NextTaskId,
            Task = task
      });
0
yyc cicero

Linqリクエストを作成しようとしましたが、結果がわかりません

        var tasks =   ( from a in DbContext.Areas
                        from g in a.Goals
                        from t in g.Tasks 
                            join oneTask in (from  t in DbContext.Tasks
                            group t by t.Id into gt
                            select new {
                                  Id = gt.Key,
                                  Sequence = gt.Min(t => t.Sequence)
                              }) on  new { t.Id, t.Sequence } equals  new { oneTask.Id,oneTask.Sequence }
                            select new {Area = a, Goal = g, Task = t})
                        .Union(         
                        from a in DbContext.Areas
                        from g in a.Goals
                        from t in g.Tasks 
                            where t.ShowAlways 
                            select new {Area = a, Goal = g, Task = t});


0
Tohm

私は現在EF Coreを持っていませんが、本当にこれを比較する必要がありますか?

タスクのクエリで十分ではないでしょうか?

ナビゲーションプロパティまたは外部キーが定義されている場合、次のようなものを使用してイメージングできます。

Tasks.Where(task => task.Sequence == Tasks.Where(t => t.GoalIdentity == task.GoalIdentity).Min(t => t.Sequence) || task.ShowAlways);

0
The-First-Tiger