Linq using join and grouping - c#

I'm trying to do something very simple.
I have two tables in my database that I would like to query using linq.
Table of Books, and table of GenreTypes. The result of this query would go to my web Api.
Here is a code snippet:
public List<BooksChart> GetBooksChart()
{
var results = from b in _dbcontext.Books
join g in _dbcontext.GenreTypes
on b.GenreTypeId equals g.Id
group g by g.Name into n
select (z => new BooksChart
{
category_name = n.Key,
value = n.Count()
}).ToList();
return results;
}
public class BooksChart
{
public string category_name;
public int value;
}
The results of the grouping "n" I would like to store them in BooksChart class to construct the Api.
This code is not compiling.
Previously, I was querying only one table of Books which I have divided into Books and GenreTypes.
My previous working code for querying Books was :
var results = _dbcontext
.Books
.GroupBy(x => x.GenreType)
.Select(z => new BooksPieChart
{
category_name = z.Key,
value = z.Count()
}).ToList();
return results;
EDIT
What I want to achieve in SQL is the following:
select count(*), g.Name
from books b, GenreTypes g
where b.GenreTypeId = g.Id
group by g.Name;

You are mixing the two syntax options of query and method. For query syntax you need to do the projection (select) like this:
return (from b in _dbcontext.Books
join g in _dbcontext.GenreTypes on b.GenreTypeId equals g.Id
group g by g.Name into n
select new BooksChart {
category_name = n.Key,
value = n.Count()
}).ToList();
The format of (z =>....) is the declaration of the labmda passed to the Select method.
Site notes:
As #Rabbi commented, since you are using EF, consider properly defining navigation properties. It will make querying simpler.
Side note for the sql - consider using joins instead of multiple tables in the from: INNER JOIN ON vs WHERE clause

The parentheses must surround the whole query, like so:
var results = (from b in _dbcontext.Books
join g in _dbcontext.GenreTypes
on b.GenreTypeId equals g.Id
group g by g.Name into n
select new BooksChart
{
category_name = n.Key,
value = n.Count()
}).ToList();
The compilation error is due to this (z => which is not needed at all.

Related

EF LINQ Group By and Sum

I'm trying to do select with group by and sum while selecting other columns using LINQ and i come out with this
var inputList = from c in db.InputItem
join o in db.ItemsDefinition on c.ItemsDefinitionID equals o.ItemsDefinitionID
group c by new { c.ItemsDefinitionID, o.ItemsAName } into g
select new
{
Name = g.Key,
Sum = g.Sum(c => c.Quantity)
};
what I'm trying to do is to preform this SQL statement
Select i.ItemsDefinitionID,
ID.ItemsAName,
sum(Quantity) as avialable
from InputItem i
Left Outer Join ItemsDefinition ID On i.ItemsDefinitionID=ID.ItemsDefinitionID
group by i.ItemsDefinitionID,ID.ItemsAName
Warm Thanks
you can do this way too:
var inputList = d.InputItem
.GroupBy(s =>s.ItemsDefinitionID, s.ItemsDefinition.AName)
.Select(g => new
{
ItemsDefinitionID=g.Key.ItemsDefinitionID,
Name = g.Key.AName,
Available= g.Sum(s =>s.Quantity),
})
.ToList();
You don't really need to do manual joins in EF if your relationships are properly defined in the model.
This query will suffice
var result = db.ItemsDefinition.Select(id => new { id.ItemsDefinitionID,
id.ItemsAName, Quantity = id.Items.Sum(i => i.Quantity) });
Either leave the SQL generation to EF or stop using EF. There's no point in using an ORM if you keep worrying about the queries it will generate.

Converting SQL to LINQ with Multiple Tables and Group

There seem to be lots of questions about SQL to LINQ, but I can't seem to find examples with joined tables and grouping; specifically with a need to get data from multiple tables.
Take this simple SQL:
SELECT
s.showId, s.showName, v.venueName, Min(dateTime) startDate
FROM
shows s
INNER JOIN venues v ON s.venueId = v.venueId
INNER JOIN showDates d ON s.showId = d.showId
GROUP BY
s.showId
The best I can come up with is the following
var ungrouped = (
from s in db.Shows
join v in db.Venues on s.VenueId equals v.VenueId
join d in db.ShowDates on s.ShowId equals d.ShowId
select new { s, v, d }
).ToList();
var grouped = (
from s in ungrouped
group s by s.s.ShowId into grp
select new
{
showId = grp.Key,
name = (from g in grp select g.s.showName).FirstOrDefault(),
venue = (from g in grp select g.v.VenueName).FirstOrDefault(),
startDate = grp.Max(g => g.d.DateTime)
}
);
This works but it feels messy. I don't like:
It being split into two statements
Having to repeatedly write (from g in grp select ...).FirstOrDefault()
Bits like s.s.ShowId
How its vastly more lines of code than the SQL
This example is a simple one, it only gets worse when I have 5+ tables to join and 10+ columns to select.
Question: Is this the best way to do this, and I should just accept it; or is there a better way to write this query?
I am not sure if you are looking for something like this but it's a bit cleaner, it's not split in 2 statements and you might find it helpful. I couldn't use a dbcontext so I used lists to make sure the syntax is correct.
var res = Shows.Join(Venues,
show => show.VenueID,
venue => venue.VenueID,
(show, venue) => new { show, venue })
.Join(ShowDates,
val => val.show.ShowID,
showdate => showdate.ShowID,
(val, showDate) => new { val.show, val.venue, showDates = showDate })
.GroupBy(u => u.show.ShowID)
.Select(grp => new
{
showId = grp.Key,
name = grp.FirstOrDefault()?.show.showName,
venue = grp.FirstOrDefault()?.venue.VenueName,
startDate = grp.Max(g => g.showDates.DateTime)
});
we need to now realation beetwen them one to one or one to many , but not too far from this answer.
var GrouppedResult = Shows.Include(x=>x.Veneu).Include(x=>x.ShowDates)
.Where(x=>x.Veneu.Any()&&x.ShowDates.Any())
.GroupBy(x=>x.ShowId)
.Select(x=>///anything you want);
or
from show in Shows
join veneu in Veneu on veneu.VeneuId equals show.VeneuId
join showDates in ShowDates on showDates.ShowId=show.ShowID
group show by show.Id into grouppedShows
select new { ///what you want };

Linq method based left join with multiple criteria

How i can obtain the similar Linq query as this SQL query using Linq method based?
SELECT * FROM F_ARTICLE A
LEFT JOIN F_ARTFOURNISS AF ON AF.AR_Ref = A.AR_Ref AND AF.AF_Principal = 1
ORDER BY A.AR_Design DESC
OFFSET 500 ROWS FETCH NEXT 100 ROWS ONLY
I'm using method based due to System.Linq.Dynamic requirements.
The mapping is like this:
I started by this but i don't know how to limit to AF_Principal = 1:
var query = context.F_ARTICLE
.Join(
context.F_ARTFOURNISS,
a => a.AR_Ref,
t => t.AR_Ref,
(a, t) => new { a, t })
.Select(
z => new ArticleItem()
{
Article = z.a,
Tarif = z.t.FirstOrDefault()
})
.OrderBy($"{field} {direction}");
return query.Skip(startIndex).Take(count).ToList();
The code is from my head but the point is using DefaultIfEmpty for doing LEFT JOIN:
var query = from farticle in context.F_ARTICLE
join b in context.F_ARTFOURNISS
on new {farticle.AR_Ref, AF_Principal = 1} equals new {b.AR_Ref, b.AF_Principal} into gj
from subres in gj.DefaultIfEmpty()
select farticle;
return query.Skip(startIndex).Take(count).ToList();
Also you can limit by AF_Principal using Where clause (I think DBMS engine will optimize the query anyway)
see if this works
var query = from F1 in context.F_ARTICLE join F2 in context.F_ARTFOURNISS
on F1.AR_Ref equals F2.AR_Ref into newF
from F3 in newF.where(f=>f.AF_Principal == 1).DefaultIfEmpty()
select f;
return query.Skip(startIndex).Take(count).ToList();
I think i found the query:
var query = context.F_ARTICLE
.GroupJoin(
context.F_ARTFOURNISS,
a => a.AR_Ref,
t => t.AR_Ref,
(a, t) => new { a, t })
.Select(
z => new ArticleItem()
{
Article = z.a,
Tarif = z.t.Where(t=>t.AF_Principal == 1).FirstOrDefault()
})
.OrderBy($"{field} {direction}");
return query.Skip(startIndex).Take(count).ToList();

C# : How to get anonymous type from LINQ result

I need to get NewsImage field and list of categories Ids that associated with the news in Many to Many relationship ... but it gives me error:
The type of one of the expressions in the join clause is incorrect.Type inference failed in the call to 'Join'.
My code looks like this
var Result1 = (from c in db.News
join d in db.Categories
on c.NewsId equals d.News.Select(l => l.NewsId)
where c.NewsId == 1
select new { c.NewsImagePath, d.CategoryId }).ToList();
Assuming you have a navigation property defining the n-n relation I would write:
var result = db.News
.Where(x => x.NewsId == 1)
.SelectMany(x => x.Categories,
(news, category) => new { news.NewsImagePath, category.CategoryId })
.ToList();
The problem is inside the on statement.
on c.NewsId equals d.News.Select( l => l.NewsId )
The Select on the right-hand side will return a IEnumerable of news, which is not what you want.
Something like this would technically work:
on c.NewsId equals d.News.Select( l => l.NewsId ).FirstOrDefault()
But it does not make sense logically.
I suspect the whole query should be built differently. I think you want to join when the category list of news contains the news item. In that case, you can't use the join statement, it would look somewhat like this:
from n in db.News
from c in db.Categories
where c.News.Select( ne => ne.NewsId ).Contains( n.NewsId )
select new { n.NewsImagePath, c.CategoryId }

Filter LINQ entity include query

I have the following two tables
Groups
Id (int)
People
Id (int)
GroupId (int, Groups.Id)
IsSelected (bit)
This will return all Groups with all their members(People) in a single query
var grps = myDatabase.Groups.Include("People");
How can I write a single query that will return all Groups with People who has been selected(IsSelected = true)?
let me know if this works
var grps = myDatabase.Groups.Select(g=> new { g, people = g.People.Where(p=>p.IsSelected)});
You will want to use the 'join' method, like this:
(from g in myDatabase.Groups
join p in myDatabase.People on g.Id equals p.GroupId
where p.IsSelected == true
select g);
This will give you all groups where there are people selected.
OR check out .Where()
Something like
var grps = myDatabase.Groups.Include("People").Where(x => x.IsSelected);
//x => !x.IsSelected for false

Categories