Join 3 tables where image exist - c#

Im trying to do an loop in my MVC5 app controller to get all timeline post and if Picture was uploaded in the post display the picture(s).
The code below get all the post and images but if i uploaded 3 pictures same post it will be looped out 3 times.
Is this not the correct way to go?
var query = (from i in db.Timeline
join u in db.Users on i.UserId equals u.Id
join f in db.UserFiles on i.Id equals f.TimelineId into ps
from f in ps.DefaultIfEmpty()
orderby i.PostDate descending
select new { i.Id, i.UserId, i.Post, i.PostDate, u.FirstName, u.ProfilePic, FileName = f == null ? "No image(s)" : f.FileName + "_thumb." + f.FileExtension }).ToList();
List<TimelineLoop> cModel = new List<TimelineLoop>();
foreach (var item in query)
{
cModel.Add(new TimelineLoop
{
Id = item.Id,
UserId = item.UserId,
Post = item.Post,
PostDate = item.PostDate,
Name = item.FirstName,
ProfilePic = item.ProfilePic,
FileName = item.FileName
});
}
return cModel;

If I look at this particular part of your query, it would produce:
from i in db.Timeline
join u in db.Users on i.UserId equals u.Id
timelinePost1 | user1
timelinePost2 | user1
timelinePost3 | user2
=> 1 user can have more than 1 timeline post.
After joining with the query below, the result in 1 will be joined to UserFiles. I assume that one timeline post can have many pictures. If you upload 3 pictures to the same post, you would expect something like this:
join f in db.UserFiles on i.Id equals f.TimelineId into ps
from f in ps.DefaultIfEmpty()
It produces:
timelinePost1 | user1 | photo1
timelinePost1 | user1 | photo2
timelinePost1 | user1 | photo3
timelinePost2 | user1 |
timelinePost3 | user2 |
Do you get this pattern in the output?
EDIT:
if you want to have 1 object per post in your result, you may need to convert the query list into dictionary, which can be done similar to query below.
List<MyObject> list = ...;
var map = list
.GroupBy(x => x.KeyedProperty)
.ToDictionary(x => x.Key, x => x.ToList());
Reference: LINQ - Convert List to Dictionary with Value as List

Related

EF duplicate values in secondary table

I'm developing a web application using Entity Framework.
I need do a select and pass values for an Ilist but it's returns duplicate values.
IQueryable<establishmentInfo> filter = (from x in db.establishments
join t in db.establishment_categories on x.id equals t.establishment
join q in db.categories on t.category equals q.id
where (x.name.ToUpper().Contains(search.ToUpper()))
select new establishmentInfo
{
id = x.id,
name = x.name,
id_category = q.id,
category = q.name,
});
IList<establishmentInfo>establishments = filter.ToList();
Establishment table
id name email
---------------------------
1 AAA a#a.com
2 BBB b#b.com
Establishment_categories
id establishment category
-------------------------------
1 1 1
2 1 2
3 2 1
Categories
id name
---------------------
1 alpha
2 beta
The problem is that return 2 establishments, one with category 1 and other with category 2. I need remove one of these.
Can anyone help?
As #NetMage said,your linq statement should return two values that are not repeated.
We can see that there are two records with establishment set to 1 in your Establishment_categories table. You can check your establishments. The id_category should be 1, the category should be alpha, the other should be id_categoryis 2, and the category should be beta.
You can see below image:
If you only want to get the first data of establishments, you can write the code as follows:
IQueryable<Establishment> filter = (from x in _context.Establishments
join t in _context.Establishment_Categories on x.Id equals t.EstablishmentId
join q in _context.Categories on t.CategoryId equals q.Id
where x.Name.ToUpper().Contains(search.ToUpper())
select new Establishment
{
Id = x.Id,
Name = x.Name,
CategoryId = q.Id,
CategoryName = q.Name,
}).Take(1);
List<Establishment> establishments = filter.ToList();
Result:
By the way, assuming that there are duplicates in your returned data, you can add the .Distinct() method after your linq to remove duplicates.

LinQ getting distinct fields with average number error

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)
};

Convert SQL to LINQ Troubles

I have been stuck on this for an embarrassing day... can't seem to convert this to linq. My issue also is that Attendee can be null.
select c.activityId, count(distinct b.attendeeId)
from Attendee a, sponsor_activity c
left outer join sponsor_attendance b
on c.ActivityId = b.ActivityId
where a.RegistrationId = 62
AND c.SponsorLevelId = 2
group by c.activityId
So far I have this code... but I am not getting distinct values
var activity_count = (from c in db.Sponsor_Activitys
where c.SponsorLevelId == pledgelvl
from a in db.Attendees.DefaultIfEmpty()
where a.RegistrationId == registration
select new { Activityid = c.ActivityId, NumAttending = db.Sponsor_Attendances.Count(x => x.ActivityId == c.ActivityId) })
.ToList();
Sponsor_Attendance
AttendanceId
AttendeeId
ActivityId
Sponsor_Activity
ActivityId
SponsorLevelId
Attendee
AttendeeId
RegistrationId
Returns:
## ActivityID ## ## NumAttending ##
2 4
3 0
4 2
2 4
3 0
4 2
2 4
3 0
4 2
Currently there are 3 attendees that have a registrationid that matches... so this is why it is repeated 3 times in the output.
First, it helps if your original queries are readable. :)
Query:
SELECT c.activityId
, COUNT(DISTINCT b.attendeeId)
FROM Attendee a
, sponsor_activity c
LEFT OUTER JOIN sponsor_attendance b
ON c.ActivityId = b.ActivityId
WHERE a.RegistrationId = 62 AND
c.SponsorLevelId = 2
GROUP BY c.activityId;
Linq:
var activity_count = (from activity in db.Sponsor_Activitys
where activity.SponsorLevelId == pledgelvl
from attendee in db.Attendees.DefaultIfEmpty()
where attendee.RegistrationId == registration
select new
{
Activityid = activity.ActivityId,
NumAttending = db.Sponsor_Attendances.Count(x => x.ActivityId == activity.ActivityId)
}).ToList();
My answer:
var query = from activity in db.Sponsor_Activitys
// Left outer join onto sponsor_attendances
join attendance in db.Sponsor_Attendances
on activity.ActivityId equals attendance.ActivityId into g
from q in g.DefaultIfEmpty()
join attendee in db.Attendees
on q.AttendeeId equals attendee.AttendeeId
where attendee.RegistrationId == registration &&
activity.SponsorLevelId == pledgelvl
select new
{
Activityid = activity.ActivityId,
NumAttending = db.Sponsor_Attendances.Count(x => x.ActivityId == activity.ActivityId)
}
Given the cartesian join (typically bad!), this might be a better example on just executing SQL rather than trying to convert to Linq.

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.

Categories

Resources