0

I have looked at many similar question on SO but seems its not that straight forward. The problem is, most of them are dealing with IEnumerable where in my case I have two IQueryable dbsets.

The situation is somewhat similar to the question here.

Student
id     Name 
1      a1
2      b1
3      c1

Images
id   Image         StudentId      Status      ModifiedOn
1    1.jpg            1           Active      2021-03-12 02:02:32.580
2    2.jpg            1           Deleted     2021-03-12 02:01:32.580
3    3.jpg            2           Deleted     2021-03-12 02:02:32.580
4    4.jpg            2           Deleted     2021-03-12 02:01:32.580

Result should be

id  Name Image
1   a1   1.jpg
2   b1   3.jpg
3   c1   NULL 

I can do this with TSQL and nested WITH qqueries, where one selects Status = Active, and the other selects Status != Active, then merge these two and select the TOP 1.

But since the requirement is to write the equivalent LINQ, I started with the below query, since I don't know a good way to do a merge of CASE WHEN on Status = Active.

        var aquery = context.Images;
        var lquery = context.Students;
        var result = from l in lquery
                     join a in aquery on l.Id equals a.StudentId into aGroup
                     from a in aGroup.OrderByDescending(m => m.ModifiedOn).Take(1)
                     select new {
                         l.id,
                         a.StudentId,
                         a.Status
                     };

This failed the dbsets are not IEnumerable. Any idea how to get the correct result?

Jimson James
  • 2,937
  • 6
  • 43
  • 78
  • That's because `IQuerable` executes sql query with all its filters on database level. Take a look here: [Returning IEnumerable vs. IQueryable](https://stackoverflow.com/questions/2876616/returning-ienumerablet-vs-iqueryablet) – Maciej Los Mar 21 '21 at 20:37
  • Correct, but I couldn't figure out how to make it work with dbset. Any idea what's the not so IQueryable in my query? – Jimson James Mar 21 '21 at 21:22

2 Answers2

0

This query should work:

var query =
    from s in context.Students
    from i in context.Images
        .Where(i => i.StudentId = s.Id)
        .OrderBy(i => i.Status == "Active" ? 0 : 1)
        .ThenByDescending(i => i.ModifiedOn)
        .Take(1)
        .DefaultIfEmpty()
    select new 
    {
        s.Id,
        s.Name,
        i.Image
    };
Svyatoslav Danyliv
  • 21,911
  • 3
  • 16
  • 32
0
 IQueryable<Image> images = context.Images.AsQueryable();
 IQueryable<Student> students = context.Students;

              
 var result =  (from st in students
                 select new
                    {
                        Id = st.Id,
                        Name = st.Name,
                        ImageName = images
                            .OrderBy(x => x.ModifiedAt)
                            .Where(x => x.Status)
                            .Where(i=> i.StudentId == st.Id)
                            .Select(x=> x.ImageName)
                            .FirstOrDefault()
                    })
                    .ToList();

But the easiest option is to define navigation field for images inside Student class:

public class Student{

   List<Image> Images {get; private set;}
}

and then:

 context.Students
         .Select(st=> new
                {
                  Id = st.Id,
                  Name = st.Name,
                  ImageName = st.Images
                                .OrderBy(x => x.ModifiedAt)
                                .Where(x => x.Status)
                                .Where(i=> i.StudentId == st.Id)
                                .Select(x=> x.ImageName)
                                .FirstOrDefault()
                 })
                 .ToList();
dantey89
  • 2,167
  • 24
  • 37