There are several problems in the LINQ query.
- The aggregation calculates the sum of differences, not the difference of sums. If either field is NULL, the difference will be NULL as well.
- The date check is reversed.
- The account is missing from the results.
The equivalent would be :
var items = context.TrxAffairs
.Where(i => i.TrxDate > trxDate)
.GroupBy(o => o.AccNo)
.Select(s => new {
o.AccNo,
Balanse=s.Sum(i=>i.Credit)-s.Sum(i=>i.Debit)
})
.ToList();
After that, calculate the grand total just once and reuse it :
var grandTotal=items.Sum(s=>s.Value);
OnGetPreviousBalance(grandTotal);
return grandTotal;
You can replace NULLs with 0s. In SQL you'd use ISNULL(Credit,0). In C#/LINQ, you can use i.Credit ?? 0:
var items = context.TrxAffairs
.Where(i => i.TrxDate > trxDate)
.GroupBy(o => o.AccNo)
.Select(s => new {
AccNo=s.Key,
Balanse=s.Sum(i=>(i.Credit??0) - (i.Debit??0))
})
.ToList();
If you only want the grand total theres' no reason to group:
var grandTotal = context.TrxAffairs
.Where(i => i.TrxDate > trxDate)
.Sum(i=>(i.Credit??0) - (i.Debit??0));
Don't rush to replace SQL with LINQ though.
LINQ isn't SQL. SQL is far more powerful when it comes to reporting and analytics. A server is far more powerful than any client and can aggregate data while filtering it.
You can get both the totals and grand total in SQL if you use ROLLUP. This can't be done in LINQ. In this case, the following query:
SELECT AccNo, SUM(Debit) - SUM(Credit) AS Balance
FROM TrxAffair
WHERE (TrxDate > '2021-01-01')
GROUP BY AccNo
WITH ROLLUP
or
SELECT AccNo, SUM(Debit) - SUM(Credit) AS Balance
FROM TrxAffair
WHERE (TrxDate > '2021-01-01')
GROUP BY ROLLUP (AccNo)
Would return the grand total as the last column with a NULL AccNo. This can have a significant impact when there are a lot of data or multiple grouping levels.
Other clauses, like CUBE or GROUPING SETS can be used to generate intermediate totals for multiple combinations. This would be very complex (and expensive) to do on the client.
For example, you could aggregate sales by Country/Product and Product/Country in a single query :
SELECT Country, Product, SUM(Sales) AS TotalSales
FROM Sales
GROUP BY GROUPING SETS ( (Country, Product), (Product, Country) );
Will generate subtotals for two different combinations. Without this, you'd have to write two different queries.
i.Credit -i.Debitwould cause problems in SQL as well> vs <SUM(Credit - Debit)you'd get a NULL if either wasnull. Instead of +100 or -100, you'd get nothing. Uses.Sum(i.Credit) - s.Sum(i.Debit)to generate the same SQL queryToList(), to avoid enumerating the results over and over again. And store the grand total in a variable to avoid calculating it twice