How to make count with linq ASP.NET C# - c#

I´m trying to make this sql request
SELECT number, COUNT(number)
FROM Bet GROUP BY number ORDER BY COUNT(number) DESC;
Code
public static List<Apuesta> GetAllNumbers(ApplicationDbContext db)
{
List<Bet> bets = (from b in db.Bet select b.Number).Count();
return bets;
}
and i want to use it in one function using linq.

To get the result you are trying to achieve you can project your query to an anonymous type:
var bets =db.Bet.GroupBy(b=>b.Number)
.Select(g=>new {Number=g.Key, Count=g.Count()})
.OrderByDescending(e=>e.Number);
Or a DTO:
public class BetDTO
{
public int Number{get;set;}
public int Count{get;set;}
}
Then project your result using that custom class:
var bets =db.Bet.GroupBy(b=>b.Number)
.Select(g=>new BetDTO{Number=g.Key, Count=g.Count()})
.OrderByDescending(e=>e.Number)
.ToList();

In addition to #octavioccl. If you like SQL-like LINQ-expressions you can use that snippet:
var bets = from b in bets group b by b.Number into g orderby -g.Key select new { Number = g.Key, Count = g.Count() });

Related

C# LINQ statement with joins, group by and having then mapped into list object

I have a model called ElectricityBillSiteExceeding that looks like this:
public class ElectricityBillSiteExceeding
{
public string GroupInvoiceNumber { get; set; }
public int ElectricityBillMainId { get; set; }
public string SiteNo { get; set; }
public decimal BillSiteTotal { get; set; }
public decimal MaximumAmount { get; set; }
}
I want to create a list of this type and use it to feed a grid on one of my pages, the purpose is to show which site has bills that exceed the max amount allowed.
I have written the SQL which will give me this dataset, it looks like this:
SELECT SUM(ElectricityBillSiteTotal),
ebs.ElectricityBillMainId,
SiteNo,
ebm.GroupInvoiceNumber,
es.MaximumAmount
FROM dbo.ElectricityBillSites ebs
LEFT JOIN dbo.ElectricityBillMains ebm
ON ebs.ElectricityBillMainId = ebm.ElectricityBillMainId
LEFT JOIN dbo.ElectricitySites es
ON ebs.SiteNo = es.SiteNumber
GROUP BY ebs.ElectricityBillMainId, SiteNo, ebm.GroupInvoiceNumber, es.MaximumAmount
HAVING SUM(ElectricityBillSiteTotal) <> 0 AND SUM(ElectricityBillSiteTotal) > es.MaximumAmount
I'm now in my repository trying to write the method which will go to the database and fetch this dataset so that I can power my grid for the user to see.
This is where I'm struggling. I have written a basic LINQ statement to select from a couple of tables, however I'm unsure how I can incorporate the group by and having clause from my SQL and also how I can then turn this IQueryable object into my List<ElectricityBillSiteExceeding> object.
What I have so far
public List<ElectricityBillSiteExceeding> GetAllElectricityBillSiteExceedings()
{
var groupedBillSitesThatExceed = from billSites in _context.ElectricityBillSites
join billMains in _context.ElectricityBillMains on billSites.ElectricityBillMainId equals
billMains.ElectricityBillMainId
join sites in _context.ElectricitySites on billSites.SiteNo equals sites.SiteNumber
//TODO: group by total, mainId, siteNo, GroupInv, MaxAmt and Having no total = 0 and total > max
select new
{
groupInv = billMains.GroupInvoiceNumber,
mainId = billMains.ElectricityBillMainId,
siteNo = billSites.SiteNo,
total = billSites.ElectricityBillSiteTotal,
max = sites.MaximumAmount
};
//TODO: Map the result set of the linq to my model and return
throw new NotImplementedException();
}
Can anyone point me in the right direction here?
The correct Linq query for your sql is the following. See Left Join to understand the DefaultIfEmpty and also the notes there about the use of ?. in the following group by.
(About the having - in linq you just provide a where after the group by)
var result = from ebs in ElectricityBillSites
join ebm in ElectricityBillMains on ebs.ElectricityBillMainId equals ebm.ElectricityBillMainId into ebmj
from ebm in ebmj.DefaultIfEmpty()
join es in ElectricitySites on ebs.SiteNo equals es.SiteNumber into esj
from es in esj.DefaultIfEmpty()
group new { ebs, ebm, es } by new { ebs.ElectricityBillMainId, ebs.SiteNo, ebm?.GroupInvoiceNumber, es?.MaximumAmount } into grouping
let sum = grouping.Sum(item => item.ebs.ElectricityBillSiteTotal)
where sum > 0 && sum > grouping.Key.MaximumAmount
orderby sum descending
select new ElectricityBillSiteExceeding
{
GroupInvoiceNumber = grouping.Key.GroupInvoiceNumber,
ElectricityBillMainId = grouping.Key.ElectricityBillMainId,
SiteNo = grouping.Key.SiteNo,
BillSiteTotal = sum,
MaximumAmount = grouping.Key.MaximumAmount
};
The error you get:
An expression tree lambda may not contain a null propagating operator
By reading this I conclude that you have an older versino of the provider and thus replace the group by code from the code above with the following:
let GroupInvoiceNumber = ebm == null ? null : ebm.GroupInvoiceNumber
let MaximumAmount = es == null ? 0 : es.MaximumAmount
group new { ebs, ebm, es } by new { ebs.ElectricityBillMainId, ebs.SiteNo, GroupInvoiceNumber, MaximumAmount } into grouping
Before getting into grouping , you need to be aware that the default join in LINQ is always an INNER JOIN. Take a look at the MSDN page How to: Perform Left Outer Joins. However, in the solution I'm presenting below, I'm using INNER JOINs since you are using fields from the other tables in your grouping and having clauses.
For reference on grouping using LINQ, check out How to: Group Query Results on MSDN.
A solution specific to your case is going to look something like:
public List<ElectricityBillSiteExceeding> GetAllElectricityBillSiteExceedings()
{
var qryGroupedBillSitesThatExceed = from billSites in _context.ElectricityBillSites
join billMains in _context.ElectricityBillMains on billSites.ElectricityBillMainId equals billMains.ElectricityBillMainId
join sites in _context.ElectricitySites on billSites.SiteNo equals sites.SiteNumber
where billSites.ElectricityBillSiteTotal != 0 && billSites.ElectricityBillSiteTotal > sites.MaximumAmount
group new { billMains.GroupInvoiceNumber, billMains.ElectricityBillMainId, billSites.SiteNo, billSites.ElectricityBillSiteTotal, sites.MaximumAmount }
by new { billMains.GroupInvoiceNumber, billMains.ElectricityBillMainId, billSites.SiteNo, billSites.ElectricityBillSiteTotal, sites.MaximumAmount } into eGroup
select eGroup.Key;
var inMemGroupedBillSitesThatExceed = qryGroupedBillSitesThatExceed.AsEnumerable();
var finalResult = inMemGroupedBillSitesThatExceed.Select(r => new ElectricityBillSiteExceeding()
{
BillSiteTotal = r.ElectricityBillSiteTotal,
ElectricityBillMainId = r.ElectricityBillMainId,
GroupInvoiceNumber = r.GroupInvoiceNumber,
MaximumAmount = r.MaximumAmount,
SiteNo = r.SiteNo,
});
return finalResult.ToList();
}
This probably will be enough. You could use AutoMapper. It will trivialize mapping to classes.
var resultList = groupedBillSitesThatExceed
.AsEnumerable() //Query will be completed here and loaded from sql to memory
// From here we could use any of our class or methods
.Select(x => new ElectricityBillSiteExceeding
{
//Map your properties here
})
.ToList(); //Only if you want List instead IEnumerable
return resultList;

How to Call Method in LINQ Select

I have the following method:
internal void DuplicateGroup(int oldGroupId, int newGroupId) {
IEnumerable<int> res = (from p in Db.table
where p.GroupID == oldGroupId
select p.packSizeID);
foreach (int ps in res)
Db.table.Add(new entityclass { GroupID = newGroupId, packSizeID = ps });
}
The method builds a List from desired IDs then adds new rescords to the same table with newGroupIDs. The question is: is it possible to call method within select?
Not in that select no, but in some selects, yes. It depends on the data source. LINQ over EF, no, but LINQ over objects, yes.

LINQ and the KeyValuePairs

I'm trying to count items in a group. So I have this LINQ to Entities query:
var qry2 = from c in qry
group c by c.Content.DownloadType into grouped
select new KeyValuePair(grouped.Key,grouped.Count());
But it doesn't work because LINQ to Entities only accepts parameter initializers or parameterless constructors. So I created a simple class to envelop the KeyValuePair type:
public class ValueCount
{
public string Key { get; set; }
public int Value { get; set; }
public KeyValuePair<string, int> ToKeyValuePair()
{
return new KeyValuePair<string, int>(this.Key, this.Value);
}
}
And changed the query to:
var qry2 = from c in qry
group c by c.Content.DownloadType into grouped
select new ValueCount
{
Key = grouped.Key,
Value = grouped.Count()
}.ToKeyValuePair();
But still doesn't work. It says that it doesn't recognizes the method ToKeyValuePair()
How can I collect KeyValuePairs from a LINQ to Entities query?
You have to call your method once you have the results back from the db and you can do that by forcing the query using ToList() and then doing a select to call your method on each item.
(from c in qry
group c by c.Content.DownloadType into grouped
select new ValueCount
{
Key = grouped.Key,
Value = grouped.Count()
}).ToList().Select(x=>x.ToKeyValuePair());
Like Eric rightly says in the comments you can get rid of your custom class and do something like
(from c in qry
group c by c.Content.DownloadType into grouped
select new
{
Key = grouped.Key,
Value = grouped.Count()
}).ToList().Select(x=>new KeyValuePair<string, int>(x.Key, x.Value));
Try adding AsEnumerable() to isolate your code from EF's:
var qry2 = from c in qry
group c by c.Content.DownloadType into grouped
select new ValueCount
{
Key = grouped.Key,
Value = grouped.Count()
}.AsEnumerable() // This "cuts off" your method from the Entity Framework,
.Select(vc => vc.ToKeyValuePair()); // letting you nicely complete the conversion in memory

LINQ Groupby a Date in a List of Object

I have a List of the following object :
public class Item
{
public DateTime DliveryDate { get; set; }
public String Order { get; set; }
}
How can i group this List<Item> by Date using LINQ?
I used the following query but didn't got excepted result in a group by dates Got a List object with a junk date of 0001/1/1
var r = from i in Items
group i by i.DliveryDate into s
select s;
This should return what you need:
var listdategroup = from x in psrAlertLogItems.AsEnumerable<Item>()
group x by x.DliveryDate.Date into s
select s;
Maybe your ChangeDateTime contains hour/time information , you should do
var listdategroup = from i in psrAlertLogItems
group i by i.DliveryDate.Date into s select s;
to get only the day component of your datetime
May be this can help u..
var sorted_lists = from sort in lists
group sort by sort.DateInfo into sorted
select sorted;
I'm getting the correct result..
have you tried it using the extension method
var itemsGruopedByDate = Items.GroupBy(item => item.DliveryDate ).ToList();
Just an alternative.
I hope its of any use.
Cheers.

How to match the results back to an array

I have an array of objects. The object has two properties a value and an index.
I use a linq to entities query with the contains keyword to bring back all results in a table that match up to value.
Now here is the issue... I want to match up the results to the object index...
what is the fastest best way to perform this. I can add properties to the object.
It is almost like I want the query results to return this:
index = 1;
value = "searchkey"
queryvalue = "query value"
From your question I think I can assume that you have the following variables defined:
Lookup[] (You look-up array)
IEnumerable<Record> (The results returned by your query)
... and the types look roughly like this:
public class Lookup
{
public int Index { get; set; }
public int Value { get; set; }
}
public class Record
{
public int Value { get; set; }
/* plus other fields */
}
Then you can solve your problem in a couple of ways.
First using an anonymous type:
var matches
= from r in records
join l in lookups on r.Value equals l.Value
group r by l.Index into grs
select new
{
Index = grs.Key,
Records = grs.ToArray(),
};
The other two just use standard LINQ GroupBy & ToLookup:
IEnumerable<IGrouping<int, Record>> matches2
= from r in records
join l in lookups on r.Value equals l.Value
group r by l.Index;
ILookup<int, Record[]> matches3
= matches2.ToLookup(m => m.Key, m => m.ToArray());
Do these solve your problem?
Just a shot in the dark as to what you need, but the LINQ extension methods can handle the index as a second paramter to the lambda functions. IE:
someCollection.Select( (x,i) => new { SomeProperty = x.Property, Index = i } );

Categories