How to select from 3 tables in a series using Linq? - c#

I have 3 tables:
Degree(Id int(PK), DegreeName string)
1 Bachelor
2 Master
3 PhD
Track(Id int(PK), DegreeId int(FK), TrackName string)
1 2 Engineer
2 1 Technician
3 1 Assistant
4 2 Physicist
5 3 Doctor
Group(Id int(PK), TrackId int(FK), GroupName string)
1 4 Group1
2 3 Group2
3 1 Group3
4 3 Group4
5 2 Group5
there is a one to many relation between Degree and Track, and another one to many relation between Track and Group.
I have this class:
Public class DegreeDetails
{
public List<Track> TrackList { get; set; }
public List<Group> GroupsList { get; set; }
}
to get all tracks these belong to bachelor degree, which are technician and assistant, I use this code:
In the controller I use this code:
DegreeDetails MyView = new DegreeDetails();
MyView.TrackList = entity.Track.Where(s => s.DegreeID == 1).ToList();
how to get a Group List of all groups these study tracks belong to bachelor degree, which should be Group2, Group4 and Group5.

Probably the best thing to do is to join these tables. You could create a view in your database, or you can join tables using Linq.
This should work:
MyView.GroupList = (from d in entity.Degree
join t in entity.Track on d.Id equals t.DegreeId
join g in entity.Group on t.Id equals g.TrackId
where d.Id == 1
select g).Distinct().ToList();
UPDATE If you want to use lambda exp, try this:
MyView.GroupList = entity.Degree.Where(d => d.Id == 1)
.Join(entity.Track, d => d.Id, t=> t.DegreeId, (d, t) => t)
.Join(entity.Group, t => t.Id, g => g.TrackId, (t, g) => g)
.Distinct().ToList();

You should join tracks with groups:
var groupList = entity.Track.Where(s=>s.DegreeID == 1).Join(entity.Group, t=>t.Id, g=>g.TrackId, (t,g)=>g).ToArray();

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 Query multiple orderby of joined tables

I have following LinQ query
var CGTABLE = (from cg in DbContext.CGTABLE
join tcg in DbContext.TCGTABLE on new { cg.CGroupId } equals new { tcg.CGroupId }
where tcg.TId == TId
select new {
CGroupId = cg.CGroupId,
CGroupCode = cg.CGroupCode,
Description = cg.Description,
C = cg.C,
DisplayOrder = cg.DisplayOrder
}).ToList();
CGTABLE = CGTABLE.OrderBy(g => g.DisplayOrder).ThenBy(g => g.C.OrderBy(c => c.CCode)).ToList();
which runs fine, but it is not doing second orderby using ThenBy ThenBy(g => g.C.OrderBy(c => c.CCode) What am I missing?
Sample data for better understanding.
Data in Tables
2
1
2
4
3
1
4
5
2
1
3
3
1
Should output after both outer and inner list ordered by
1
1
2
3
4
2
1
2
4
5
3
1
3
But Currently it is showing
1
4
5
2
1
2
1
2
4
3
3
3
1
You didn't want to order the main list, you are looking for a way to order inner list inside of outer one, I think.
So below code will do it for you:
var CGTABLE = (
from cg in DbContext.CGTABLE
join tcg in DbContext.TCGTABLE on new { cg.CGroupId } equals new { tcg.CGroupId }
where tcg.TId == TId
select new {
CGroupId = cg.CGroupId,
CGroupCode = cg.CGroupCode,
Description = cg.Description,
C = cg.C.OrderBy(x => x.CCode),
DisplayOrder = cg.DisplayOrder
}).ToList();
CGTABLE = CGTABLE.OrderBy(g => g.DisplayOrder).ToList();

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.

GroupBy Multiple Tables Using Foreign Key

I have two tables, and I calculate Post Views from Views table using ViewDate column and then I want to get PostTItle using .GroupBy for Entity Framework using foreign key PostID.
Posts table:
PostID PostTitle
--------------------
1 post one
2 post two
3 post three
4 post four
5 post five
6 post six
Views table:
ViewID ViewDate PostID
---------------------------------------
1 2015 07 17 19:00:00 1
2 2015 07 17 20:00:00 1
3 2015 07 17 21:00:00 2
4 2015 07 18 19:00:00 2
5 2015 07 19 19:00:00 2
6 2015 07 21 19:00:00 1
7 2015 07 23 19:00:00 2
so far this is what I have done
return _db.ObjectSet.Where(p => DateTime.Now >= EntityFunctions.AddDays(p.ViewDate, -14))
.GroupBy(y => y.PostID, y => y.ViewDate, (ID, Date) => new ExampleViewModel
{
Post_ID = ID,
View_Date = Date.Count()
}).OrderByDescending(z => z.View_Date).Take(5);
but using this solution I can only assign Post_ID and View_Date to ExampleViewModel, How can I get the PostTitle using the foreign key?
Note: I am trying to get most viewed (Hot) Posts in last 14 days
Please help
Expected Output:
Title:post one, Id:1, Views:3
Title:post two, Id:2, Views:4
One solution could be applying a join between those entities and include the PostTitle in the fields you want to group:
var query= (from v in db.Views
join p in db.Posts on v.PostID equals p.Id
where DbFunctions.DiffDays(v.ViewDate,DateTime.Now)<=14
group new{v,p} by new {v.PostID, p.PostTitle, v.ViewDate} into g
let count=g.Count()
orderby count descending
select new{ Post_ID=g.Key.PostID, View_Date=count, Title= g.Key.PostTitle}
).Take(5);
As you can see, I'm using DbFunction class instead EntityFunction. The DbFunctions class was introduced in Entity Framework 6 and it is shipped separately from the .NET Framework. For any new applications using versions of EF starting with 6.0, you should use the DbFunctions class. Anyway, if you don't want to use now that class, you could also use the EntityFunctions.DiffDays method.
Now if both entities are related:
public class Post
{
public int ID{get;set;}
// ...
public virtual ICollection<View> Views{get;set;}
}
public class View
{
public int ID{get;set;}
public int PostID{get;set;}
// ...
public virtual Post Post{get;set;}
}
You could also do this:
var query= (from v in db.Views
where DbFunctions.DiffDays(v.ViewDate,DateTime.Now)<=14
group v by new {v.PostID, v.ViewDate} into g
let count=g.Count()
orderby count descending
select new{ Post_ID=g.Key.PostID, View_Date=count, Title= g.First().Post.PostTitle}
).Take(5);
Update 1
To avoid use EntityFunctions class you can subtract 14 days to the current date and compare directly both dates in your query:
var date = DateTime.Now.AddDays(-14);
var query= (from v in db.Views
join p in db.Posts on v.PostID equals p.Id
where v.ViewDate>=date
group new{v,p} by new {v.PostID, p.PostTitle, v.ViewDate} into g
let count=g.Count()
orderby count descending
select new{ Post_ID=g.Key.PostID, View_Date=count, Title= g.Key.PostTitle}
).Take(5);
Update 2
That is because you're grouping by date. To obtain the result you're expecting you need to remove that field from the elements you are grouping:
var query= (from v in db.Views
join p in db.Posts on v.PostID equals p.Id
where v.ViewDate>=date
group new{v,p} by new {v.PostID, p.PostTitle} into g
let count=g.Count()
orderby count descending
select new{ Post_ID=g.Key.PostID, View_Date=count, Title= g.Key.PostTitle}
).Take(5);
If you have the Posts table on your context you can retrieve it from there by the PostId:
return _db.ObjectSet.Where(p => DateTime.Now >= EntityFunctions.AddDays(p.ViewDate, -14))
.GroupBy(y => y.PostID, y => y.ViewDate, (ID, Date) => new ExampleViewModel
{
Post_ID = ID,
View_Date = Date.Count(),
Title = _db.Posts.Find(ID).PostTitle
}).OrderByDescending(z => z.View_Date).Take(5);

Sorting a list based on another attribute in another list

I have the objects as below:
public class CustomerSequence
{
public string CustomerName { get; set; }
public int Sequence { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Component { get; set; }
}
Let's say I have 2 Lists Object
Customer: CustomerSequence:
Id Name Component CustomerName Sequence
1 AAA AAAAAA AAA 2
2 BBB BBBBBBB BBB 4
3 CCC CCCCC CCC 1
DDD 3
As you can see there is no DDD in List.
I want to sort the List Customer based on the List CustomerSequence
Result is:
Customer:
Id Name Component
3 CCC CCCCC
1 AAA AAAAAA
2 BBB BBBBBBB
Anyone can help me please.
Join both sequences on customer name, then order by sequence value:
from c in customers
join cs in customerSequences
on c.Name equals cs.CustomerName
orderby cs.Sequence
select c;
Lambda syntax is not that beautiful, and it will look like
customers.Join(customerSequences,
c => c.Name, cs => cs.CustomerName, (c,cs) => new { c, cs })
.OrderBy(x => x.cs.Sequence)
.Select(x => x.c)
Internally join uses lookup for second sequence, which is much more effective then linear search with Where.
If it is possible that there is no CustomerSequencs matching customer, or there is more than one match, then use group join:
from c in customers
join cs in customerSequences
on c.Name equals cs.CustomerName into g
orderby g.Select(cs => cs.Sequence).FirstOrDefault()
select c
This query uses 0 form missing sequences, and first matched value if there is more than one sequence for customer.
Try this
Customer.OrderBy(x => CustomerSequence.Where(y => y.CustomerName == x.Name)
.Select(y => y.Sequence)
.FirstOrDefault())
Alternatively you can use a join which would be better if the source was a database
var sorted =
from c in customer
join csj in customerSequence on c.Name equals csj.CustomerName into customerSequenceJoined
from cs in customerSequenceJoined.DefaultIfEmpty()
orderby cs == null ? 0 : cs.Sequence
select c;
The cs == null ? 0 : cs.Sequence deals with the case when there is no matching record in the sequence collection. You could use int.MaxValue if you want these items to appear last.
Use Join
var customers = from cust in Customer
join cust_seq in CustomerSequence
on cust.Name equals cust_seq.CustomerName
orderby cust_seq.Sequence
select cust;
I tend to use a dictionary for this sort of thing.
var customerSequence =
customerSequences
.ToDictionary(x => x.CustomerName, x => x.Sequence);
var sortedCustomers =
customers
.OrderBy(x => customerSequence[x.Name])
.ToList();

Categories

Resources