LinQ getting distinct fields with average number error - c#

I am having a "Type expected" error which I have no idea why.
My query simply link 3 tables together while trying to get the distinct package and average number of rating.
The outcome should be like this
| PackageName | Average Rating |
| SG | 4 |
| USA | 4 |
IQueryable<Recommendation> recommendationQuery = db.Recommendations;
IQueryable<Booking> bookingQuery = db.Bookings;
IQueryable<Package> packageQuery = db.Packages;
recommendationQuery = (from recommendationItem in recommendationQuery
join bookingItem in bookingQuery
on recommendationItem.BookingId equals bookingItem.BookingId
join packageItem in packageQuery
on recommendationItem.Booking.PackageId equals packageItem.PackageId
select recommendationItem).GroupBy(c => c.Booking.Package.PackageTitle)
.Select(c => new ( c.Key, c.Average(d=>d.Rating)));
The type expected occurs in the .Select(c => new (.....
May I know if I have query it wrongly?
Because
1) I inner joined all my 3 tables together
2) Assuming I have all the table joined, I tried to group them by PackageName to distinct the name to one name
3) I tried to select the average sum of the rating of the same package.
any idea if there's a better solution for this?
database class diagram
Solution error

You need to store results back into a new variable to match your new type:
var results = from recommendationItem in recommendationQuery
join bookingItem in bookingQuery
on recommendationItem.BookingId equals bookingItem.BookingId
join packageItem in packageQuery
on recommendationItem.Booking.PackageId equals packageItem.PackageId
group recommendationItem
by recommendationItem.Booking.Package.PackageTitle
into grp
select new
{
PackageName = grp.Key,
AverageRating = grp.Average(d => d.Rating)
};

Related

How to apply self join in Linq Query?

Books Table
Id VendorId ASIN Price
-- -------- ---- ------
1 gold123 123 10
2 sil123 123 11
3 gold456 456 15
4 gold678 678 12
5 sil456 456 12
6 gold980 980 12
I want to write a linq query which will return me rows for which corresponding to every gold if sil vendor id not exist. The last three digit of vendor Id is corresponding ASIN column in that row.
Ex- For gold123 corresponding sil123 exist so that row will not be returned but for gold678 and gold980 corresponding sil not exist. So those rows will be returned.
I tried following
var gold = _repository.Query<Books>().Where(x =>
x.VendorId.Contains("gold"))
.OrderBy(x => x.Id).Skip(0).Take(500).ToList();
var asinsForGold = gold.Select(x => x.ASIN).ToList();
var correspondingSilver = _repository.Query<Books>().Where(x =>
x.VendorId.Contains("sil")
&& asinsForGold.Contains(x.ASIN)).ToList();
var correspondingSilverAsins = correspondingSilver.Select(x => x.ASIN).ToList();
var goldWithoutCorrespondingSilver = gold.Where(x =>
!correspondingSilverAsins.Contains(x.ASIN));
Can We apply self join or better way to get result only in one query instead of two query and several other list statement.
It's just another predicate, "where a corresponding silver vendor doesn't exist":
var goldWoSilver = _repository.Query<Books>()
.Where(x => x.VendorId.Contains("gold"))
.Where(x => !_repository.Query<Books>()
.Any(s => s.ASIN == x.ASIN
&& s.VendorId.Contains("sil"))
.OrderBy(x => x.Id).Skip(0).Take(500).ToList();
In many cases this is a successful recipe: start the query with the entity you want to return and only add predicates. In general, joins shouldn't be used for filtering, only to collect related data, although in that case navigation properties should be used which implicitly translate to SQL joins.
See if it helps -
var goldWithoutCorrespondingSilver = from b1 in books
join b2 in books on b1.ASIN equals b2.ASIN
where b1.VendorId.Contains("gold")
group b2 by b1.VendorId into g
where !g.Any(x => x.VendorId.Contains("sil"))
select g.FirstOrDefault();
What I have done is -
Selected records with matching ASIN
Grouped them by VendorID
Selected ones which do not have sil

How to do Many To Many LINQ entity grouping queries?

I can see many examples on this site of the sort of queries I'm after, but I can't relate to them and how they work. I was wondering if you could help me.
I have set up my many-to-many tables and relationships with entity designer in Visual Studio.
tblQuotes
ID | QuoteNo | Date
tblItems
ID | PartNo | Desc
tblSuppliers
ID | Supplier | email
tblQIS (quotes items suppliers)
ID | SupplierID | QuoteID | ItemID
I've put some test data in and have begun to try to type this, but I think I first need to group by quoteNo then group by supplier, to get the details in the correct view.
var tblQuotes = from d in db.tblQuotes_Items_Suppliers
.Include(t => t.tblItems)
.Include(t => t.tblQuotes)
.Include(t => t.tblSuppliers)
group by (d.QuoteID,d.SupplierID)
select d;
Can anyone help me out?
you could try this:
var tblQuotes =
from d in db.tblQuotes_Items_Suppliers // or tblQIS whatever the name
group d by new
{
d.QuoteID,
d.SupplierID
} into g
select g;
that would give you Quotes grouped by both QuoteID and SupplierID
Update
the tblQuotes is a list (IQueryable) of grouped quotes, so you can access other entities as follow:
var firstGroupOfQuotes = tblQuotes.First(); // will give you the first group of quotes
var firstQuote = firstGroupOfQuotes.First(); // will give you the first quote in the first group
var item = firstQuote.tblItems; // will give you the item of this quote
var partNo = item.PartNo; // will give you the PartNo of this item

How to SUM up results by column value in db query result

My database has a sales table with entries like so:
_____________________________________
| id | title_id | qty |
-------------------------------------
| 0 | 6 | 10 |
-------------------------------------
| 1 | 5 | 5 |
-------------------------------------
| 2 | 6 | 2 |
-------------------------------------
Title_id is Foreign key pointing to Titles table which is as follows:
_____________________________________
| id | title_id | title |
-------------------------------------
| 0 | 5 | Soda |
-------------------------------------
| 1 | 6 | Coffee |
-------------------------------------
I want to find top 5 sold products wich means i need to calculate the qty value for each product for all it's entried in sales table then order the result by qty in descending order and limit the select to 5.
However I'm new to C# ASP.NET and somewhat new to SQL. I dont know how to do this with LINQ.
This is my code so far:
var getIds = (from sale in db.sales
join tit in db.titles on sale.title_id equals tit.title_id
group sale by sale.qty into result
orderby result.Sum(i => i.qty) descending
select new Publication
{
PubID = sales.title_id, Title = tit.title
}
).Take(5);
Assuming you have a navigation property Sale.Title, something like this should do:
var tops =
db.Sales
.GroupBy( o => o.Title )
.Select( o => new { Title = o.Key, Sum = o.Sum( x => x.Quantity ) } )
.OrderByDescending( o => o.Sum )
.Take( 5 )
.ToList();
tops is then a list of an anonymous type with two properties: the Title object and the sum of the quantities.
You can then get the values like this:
foreach( var top in tops )
{
int titleId = top.Title.title_id;
string title = top.Title.title;
int sumOfQuantities = top.Sum;
...
If you just want the top Title objects, can can select them like this:
List<Title> topTitles = tops.Select( o => o.Title ).ToList();
var result= (from p in sales
let k = new
{
Name = p.Name
}
group p by k into t
orderby Name descending
select new
{
Name = t.Name,
Qty = t.Sum(p => p.Qty)
}).Take(5);
If the entries in the Sales table are more than one per item (ie: in your example you have 'Soda' 10 + 'Soda' 2, then you need to GroupBy(), using the name as the key (or it's related id if it's in another table), but not the qty.
var topSales = db.sales.GroupBy(x => x.title)
.Select(g => new
{
Title = g.Key,
Qty = g.Sum(x => x.qty)
})
.OrderByDescending(x => x.Qty)
.Select(x => new Publication
{
PubID = x.Title.title_id,
Title = x.Title.title1
})
.Take(5)
.ToList();
Note that I've omitted the join statement assuming that you have a foreign key between sales.title_id -> title.id, and you are using LINQ to SQL. Also note that I've avoided using the query syntax in favor of the chained method syntax, I think it's much clear in this use case (although not always true, ie: cross-joins).
Also, SQL and LINQ have some similarities but don't let the names of clauses/methods fool you, LINQ is not SQL, IMHO, Microsoft just tried to make people comfortable by making it look similar ;)
EDIT: fixed GroupBy()
var result= (from p in sales
let k = new
{
Name = p.Name
}
group p by k into t
select new
{
Name = t.Name,
Qty = t.Sum(p => p.Qty)
}).OrderByDescending(i => i.Qty).Take(5);
You need to look at GroupBy; this will give you what you need
http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b

LINQ GroupBy, whilst keeping all object fields

I've currently got this sample table of data:
ID | Policy ID | History ID | Policy name
1 | 1 | 0 | Test
2 | 1 | 1 | Test
3 | 2 | 0 | Test1
4 | 2 | 1 | Test1
Out of this, I want to group by the Policy ID and History ID (MAX), so the records I want to be kept are ID's 2 and 4:
ID | Policy ID | History ID | Policy name
2 | 1 | 1 | Test
4 | 2 | 1 | Test1
I've tried to do this in LINQ and stumbling on the same issue every time. I can group my entities, but always into a group where I have to re-define the properties, rather than have them kept from my Policy objects. Such as:
var policies = _context.Policies.GroupBy(a => a.intPolicyId)
.Select(group => new {
PolicyID = group.Key,
HistoryID = group.Max(a => a.intHistoryID)
});
This simply just brings out a list of objects which have "Policy ID" and "History ID" within them. I want all the properties returned from the Policies object, without having to redefine them all, as there are around 50+ properties in this object.
I tried:
var policies = _context.Policies.GroupBy(a => a.intPolicyId)
.Select(group => new {
PolicyID = group.Key,
HistoryID = group.Max(a => a.intHistoryID)
PolicyObject = group;
});
But this errors out.
Any ideas?
Group by composite key
_context.Policies.GroupBy(a => new {a.intPolicyId, *other fields*}).Select(
group=> new {
PolicyId = group.Key.intPolicyId,
HistoryId = group.Max(intHistoryId),
*other fields*
}
);
Another way - grab histories, than join back with the rest of the data, something like this (won't work out of the box, will require some refining)
var historyIDs = _context.Policies.GroupBy(a=>a.intPolicyId).Select(group => new {
PolicyID = group.Key,
HistoryID = group.Max(a => a.intHistoryID)
});
var finalData = from h in historyIDs
join p in _context.Policies on h.intPolicyId equals p.intPolicyId
select new {h.HistoryId, *all other policy fields*}
And yet another way, even simpler and not require a lot of typing :):
var historyIDs = _context.Policies.GroupBy(a=>a.intPolicyId).Select(group => new {
PolicyID = group.Key,
HistoryID = group.Max(a => a.intHistoryID)
});
var finalData = from h in historyIDs
join p in _context.Policies on h.PolicyId equals p.intPolicyId && h.HistoryId equals p.HistoryId
select p
Basically it's somewhat equivalent to the following SQL query:
select p.*
from Policy p
inner join (
select pi.policyId, max(pi.historyId)
from Policy pi
group by pi.policyId
) pp on pp.policyId = p.policyId and pp.historyId = p.historyId
In LINQ to Objects, I'd do this as
var policies = _context.Policies
.GroupBy(a => a.intPolicyId)
.Select(g => g.OrderByDescending(p => p.intHistoryID).First());
but your _context impleis there might be a database involved and I'm not 100% sure this will translate.
Basically it groups by the policy ID as you'd expect, then within each group orders by history ID and from each group selects the row with the highest history ID. It returns exactly the same type as is found in Policies.

Doing a LINQ join, then group by, then a sum on two different columns

I am using LINQ to SQL in a project and I am having an issue doing both a join and a group by to do a comparison between the two fields that are in each table.
Here is what my query looks like:
var q =
(from ii in
(from a in table1
join b in table2 on a.BudgetUnitID equals b.BudgetUnitID
select new { BT = a.Amount, BA = b.Amount, BUID = a.BudgetUnitID, BU = a.BudgetUnit.BudgetUnitName })
group ii by new {ii.BUID} into g
select new
{
BudgetUnit = g.Key,
Budget = g.Sum(x => x.BA),
Actual = g.Sum(x => x.BT),
Variance = g.Sum(x => x.BA) - g.Sum(x => x.BT)
}).ToList();
I am am going to bind this to a grid view on a web page. My problem is that I am not getting the totals correct on one of the columns.
Your help is much appreciated.
Your problem is likely that the Budget amount is incorrect, because you are summing them for every combination. For example, if you had budget data looking like this:
Budget | Amount
Dev | 2500
And line items like this:
Budget | Amount
Dev | 1000
Dev | 750
Then you are combining them together in your inner query like this:
BA | BT
2500 | 1000
2500 | 750
And then summing them together, which gives you
Budget | Actual
5000 | 1750
In this case, the budget is twice as large as it should be, because you are adding all the entries together per combination. You want to sum the individual tables first, before joining the results together so that you don't have this "duplication" issue in your sub-query data:
var q =
(from ii in
(from a in table1 group a by a.BudgetUnitID into g
select new { BudgetUnitID = g.Key, Amount = g.Sum(x => x.Amount) })
join jj in
(from b in table2 group b by b.BudgetUnitID into g
select new { BudgetUnitID = g.Key, Amount = g.Sum(x => x.Amount) })
on ii.BudgetUnitID equals jj.BudgetUnitID
select new { Actual = ii.Amount, Budget = jj.Amount, Variance = jj.Amount - ii.Amount, BUID = ii.BudgetUnitID }
).ToList();

Categories

Resources