0

I have an sql database table named hate, I want to get each items name and its count by linq query that is my codes:

var qLocation = (from L in db.Hato
         where L.HatoRecDate >= startDate && L.HatoRecDate <= endDate
                group L by L.HatoLocation into g
              select new { HatoLocation = g.Key, count = g.Count() })
.OrderByDescending(o => o.count).ToList();

var l = qLocation[0].HatoLocation;
var c = qLocation[0].count;

It gives me item name; but shows 0 result for any item count please, tell me where is wrong with my code?

Update

After feedback I have captured the following output, what is interesting is that it is only ever the last record in the set that has a zero count:

Annotated code

Chris Schaller
  • 13,704
  • 3
  • 43
  • 81
  • What is `Hato` def? What are `qLocation` and `L` and `l` and `g` and `o` and `c`? [C# Naming Conventions](https://www.c-sharpcorner.com/UploadFile/8a67c0/C-Sharp-coding-standards-and-naming-conventions/) and [C# Coding Standards and Naming Conventions](https://github.com/ktaranov/naming-convention/blob/master/C%23%20Coding%20Standards%20and%20Naming%20Conventions.md) and [C# Coding Conventions (C# Programming Guide)](https://learn.microsoft.com/dotnet/csharp/programming-guide/inside-a-program/coding-conventions) –  Jun 01 '21 at 10:15
  • Which type has `HatoLocation`? – Svyatoslav Danyliv Jun 01 '21 at 10:24
  • HatoLocation is string – Nuzheen Kazim Jun 01 '21 at 10:25
  • Then I do not see problems with your LINQ query. Analyze generated SQL. – Svyatoslav Danyliv Jun 01 '21 at 12:36
  • What database engine are you using? I can't see how it is possible to get a count of zero – Chris Schaller Jun 03 '21 at 01:56
  • Sql server 2016 – Nuzheen Kazim Jun 03 '21 at 22:17
  • Your edit is a little bit confusing, We have already highlighted that this is an issue with DbContext, however introducing strange stored proc concepts makes this post harder to read. I would roll that edit back, instead please stick with the linq, we would like you to capture the generated SQL _before_ you execute on the DbContext. Something in your setup is not correct and we want to help. If you are expecting 3 rows, please include more example data records because there should only be 2 rows in the data you have provided. – Chris Schaller Jun 03 '21 at 22:46

2 Answers2

0

Your code looks OK, I see no syntax issues with the query itself, what you need is a few tricks that will help you debug this.

When you run this with an In-Memory record set it behaves as expected, this means that the issue is in the generated SQL that your Linq query is translated into via the DbContext.

As a proof for your In-Memory, review this fiddle: https://dotnetfiddle.net/Widget/jxKNG5

Although it is not good practice for production code, one way to work around, and prove this issue is a SQL issue is by reading the data into memory before executing the group by. The results of an IQueryable<T> expression can be loaded into memory using .ToList().

Rather than calling .ToList() on the entire table, if the filter conditions are not in question, call .ToList() after the filter criteria. If you accidentally leave this in your code after your debug session it is going to have less impact than if you were reading every record from the database

#region A safer way to bring the recordset into memory for debugging
// Build the query in 2 steps, first create the filtered query
var filteredHatoQuery = from L in db.Hato
                        where L.HatoRecDate >= startDate && L.HatoRecDate <= endDate
                        select L; 
                        // you could also consider only projecting the columns you need
                        // select new { L.HatoRecDate, L.HatoLocation };             
// then operate on the data
var qLocation = (from L in filteredHatoQuery.ToList()   // remove the .ToList() to query against the DB      
                 group L by L.HatoLocation into g
                 select new { HatoLocation = g.Key, count = g.Count() })
                .OrderByDescending(o => o.count).ToList();
                 
#endregion A safer way to bring the recordset into memory for debugging

To be honest, I had a really hard time re-creating a query where you could possibly get a Count() of zero. Zero items means no records in the group, which would normally prevent the group header from returning at all, in fact I tried a lot of different angles to this, and really can't figure it out.

There are two complicating factors for manually debugging a query like this:

  1. Linq / C# group by is vastly different to SQL GROUP BY. In C# grouping simply splits the results into sub-arrays, all the records are still in the output, but in SQL the GROUP BY doesn't return all the records, it only returns the aggregate group results. To do this properly, the grouping should be realised in SQL as a nested query, it won't necessarily always involve a SQL GROUP BY.

    • Either way, the resulting SQL will NOT be as simple as this:

      SELECT HatoLocation, COUNT(*)
      FROM Hato
      WHERE HatoRecDate >= '2021-05-21' AND HatoRecDate <= '2021-05-24'
      GROUP BY HatoLoction
      
  2. You are ordering by the results of an aggregate within a filter. This is not always a big deal, but it can often lead to complications in SQL if you are not also using a limiting factor like TOP. As a general proposition, if the sorting only affects the rendered output, and not the functional logic, then you should leave the sort process to the renderer. Or at the very least, sort In-Memory, not in the SQL.

The original query would evaluate into SQL similar to this:
(I have substituted the Start and end parameters @p_linq_0 and @p_linq_1)

SELECT 
[Project1].[C2] AS [C1], 
[Project1].[HatoLocation] AS [HatoLocation], 
[Project1].[C1] AS [C2]
FROM ( SELECT 
    [GroupBy1].[A1] AS [C1], 
    [GroupBy1].[K1] AS [HatoLocation], 
    1 AS [C2]
    FROM ( SELECT 
        [Extent1].[HatoLocation] AS [K1], 
        COUNT(1) AS [A1]
        FROM [dbo].[Hato] AS [Extent1]
        WHERE ([Extent1].[HatoRecDate] >= '2021-05-21') AND ([Extent1].[HatoRecDate] <= '2021-05-24')
        GROUP BY [Extent1].[HatoLocation]
    )  AS [GroupBy1]
)  AS [Project1]
ORDER BY [Project1].[C1] DESC

But even that is not going to result in a count of zero. I can only assume that OPs runtime environment or database introduces some other factor that has not been taken into account for this exploration.

In Linq to Entities you can get the resulting SQL for queries that have not been read into memory simply by calling .ToString() on the query, or by using the inspector tool during a debug session. There is a good discussion in this post Get SQL query from LINQ to SQL?

For debugging purposes, it is a good idea to separate the linq query from the resulting enumerated or In-Memory result set, also in this example we have specifically isolated out the sort to occur after the .ToList() and the SQL has been written to the debug output.

var qLocationQuery = from L in db.Hato
                     where L.HatoRecDate >= startDate && L.HatoRecDate <= endDate
                     group L by L.HatoLocation into g
                     select new { HatoLocation = g.Key, count = g.Count() };

System.Diagnostics.Debug.WriteLine("Hato Query SQL:");
System.Diagnostics.Debug.WriteLine(qLocationQuery.ToString());

var qLocation = qLocationQuery.ToList();
// now perform the sort, this simulates leaving the sort to the rendering logic.
qLocation = qLocation.OrderByDescending(o => o.count).ToList();

Please update your post with the resulting SQL so we can further explore this!

Update

I've updated the fiddle with an actual DbContext implementation, I still cannot produce a grouping with a count of zero. https://dotnetfiddle.net/G4RvUV

This shows how to extract the SQL query, but it shows there is something else wrong with your code. We either need to see more of the data, more of the schema, or a copy of the data without the grouping (as shown in the fiddle) so we can provide more assistance.

Chris Schaller
  • 13,704
  • 3
  • 43
  • 81
  • 1
    Thanks dear Chris Schaller. the problem was solved by your help. Thank you again – Nuzheen Kazim Jun 04 '21 at 16:37
  • If it helped, please mark it as an answer. I'm still very curious though, how did you solve it specifically and I'd love to see the SQL, I just can't see how you get zeros for the counts, unless it was a null value comparison issue. – Chris Schaller Jun 04 '21 at 23:44
-1

Try this... Do the .ToList() and after that do the group by.

Gael Abreu
  • 36
  • 1
  • 5
  • 'var qryLst = (from L in db.Hato where L.HatoRecDate >= startDate && L.HatoRecDate <= endDate select L).ToList();' ' var qryCnt = (from C in qryLst group C by C.HatoLocation into G select new { location = G.Key, count = G.Count() }).OrderByDescending(o => o.count).ToList(); string l = qryCnt[0].location; int c = qryCnt[0].count;' – Nuzheen Kazim Jun 02 '21 at 18:10
  • I did this, but the result didn't change – Nuzheen Kazim Jun 02 '21 at 18:14