How to join two tables with linq? - c#

I am trying to join two of my tables with linq based on an id, so far unseccesfully.
Here is how my models look :
public class WorkRole
{
public int WorkRoleId { get; set; }
public string RoleName { get; set; }
public string RoleDescription { get; set; }
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<WorkRolesUsersDetails> WorkRolesUsersDetails { get; set; }
}
public class WorkRolesUsersDetails
{
public int WRUDId { get; set; }
public int? WorkRoleId { get; set; }
public string UserDetailsId { get; set; }
public virtual WorkRole WorkRole { get; set; }
public virtual UserDetails UserDetails { get; set; }
public DateTime FocusStart { get; set; }
public DateTime FocusEnd { get; set; }
public bool isActive { get; set; }
}
I am trying to get in one view WorkRoleId, RoleName, RoleDescription and CompanyId from the first table and UserDetailsId, FocusStart, FocusEnd and isActive from the second table.
The farthest i got with my ideas was :
var query = db.WorkRoles.Join(db.WorkRolesUsersDetails,x => x.WorkRoleId,y => y.WorkRoleId,(x, y) => new { wr = x, wrud = y });
But sadly, it didn't work. I just don't know enough linq and couldn't get much out of other questions/answers here. Please, help.

Code for joining 2 tables is:
var list = db.WorkRoles.
Join(db.WorkRolesUsersDetails,
o => o.WorkRoleId, od => od.WorkRoleId,
(o, od) => new
{
WorkRoleId= o.WorkRoleId
RoleName= o.RoleName,
RoleDescription= o.RoleDescription,
CompanyId= o.CompanyId,
WRUDId= od.WRUDId,
UserDetailsId= od.UserDetailsId,
FocusStart=od.FocusStart,
FocusEnd=od.FocusEnd
})

If you are using EF may I suggest the Includes statement it works wonders. IF you have a foreign key assigned. It basically gets the other data with it.
static void Main(string[] args)
{
using (var context = new TesterEntities())
{
var peopleOrders = context.tePerson.Include("teOrder").First(p => p.PersonId == 1).teOrder.ToList();
peopleOrders.ForEach(x => Console.WriteLine($"{x.OrderId} {x.Description}"));
}
}
Combining manually without navigation context option.
public class Student
{
public int StudentID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<StudentTestScore> Scores { get; set; }
}
public class StudentTestScore
{
public int StudentID { get; set; }
public int Score { get; set; }
}
class Program
{
static void Main(string[] args)
{
var students = new List<Student>
{
new Student { StudentID = 1, FirstName = "Brett", LastName = "X" },
new Student { StudentID = 2, FirstName = "John", LastName = "X" }
};
var grades = new List<StudentTestScore> { new StudentTestScore { StudentID = 1, Score = 98 } };
var combined = students.Join(grades, x => x.StudentID, y => y.StudentID,
(x, y) => new
{
Student = $"{x.FirstName} {x.LastName}",
Grade = y.Score
}).ToList();
combined.ForEach(x => Console.WriteLine($"{x.Student} {x.Grade}"));
Console.ReadLine();
}

Related

ASP.NET Core populate selectlist with union result

var a = _context.Components.Select(c => c.Week).Union(_context.FinishGoods.Select(f => f.Week));
WeekFilter = new SelectList(a, "Week", "Week");
this does not work. What do i need to make the union result work on select list? I Tried ToList(), AsEnumerable()
this has worked
Entries = _con.Query<ExcelViewModel>(query).ToList();
var selectList = Entries
.Select(e => new
{
Id = e.ComponentWeek.Value.ToString(),
Name = e.ComponentWeek.Value.ToString()
}
).Union(Entries.Select(e => new
{
Id = e.FinishGoodWeek.Value.ToString(),
Name = e.FinishGoodWeek.Value.ToString(),
}));
WeekFilter = new SelectList(selectList, "Id", "Name");
EDIT
here are the objects to union they are basically the same in structure
public class FinishGood
{
public int Id { get; set; }
public int? Quantity { get; set; }
public int? Week { get; set; }
public int EntryId { get; set; }
public Entry Entry { get; set; }
}
public class Component
{
public int Id { get; set; }
public int? Quantity { get; set; }
public int? Week { get; set; }
public int EntryId { get; set; }
public Entry Entry { get; set; }
}
Please try this one it works fine.
var a = _context.Components.Select(c => new { Week = c.Week.Value })
.Union(_context.FinishGoods.Select(f => new { Week = f.Week.Value })).ToList();
WeekFilter = new SelectList(a, "Week" , "Week");

How can change a MYSQL Join Query in Linq Methods

I write a MySql join code, and want to retrive same value from the Dotnetcore linq methods.
My Join code is below:
SELECT GL.Id AS GradeLevels,
CRS.Name AS CourseName,
GL.Title AS GradlevelName,
AVG (ASTSTU.ObtainedMarks)
FROM GradeLevels GL
INNER JOIN Courses AS CRS ON CRS.GradeLevelsID = GL.Id
INNER JOIN Units AS UNT ON UNT.CourseID = CRS.ID
INNER JOIN Lessons AS LSN ON LSN.UnitsId = UNT.Id
INNER JOIN Assignments AS AST ON AST.LessonId = LSN.id
INNER JOIN AssignmentStudents AS ASTSTU ON ASTSTU.AssignmentId = AST.id
WHERE CRS.SchoolSystemsID = "08d6a1f2-26df-4ad5-25d3-2a26960aa3fd" -- School System id.
GROUP BY GL.Id;
Now I want to change above MySQL Join into Dotnet core linq method to create an API that will be Showing, I try to write code for this
public async Task<ICollection<GradeLevels>> GetSchoolSystemGradLevelsAverage(Guid schoolSystemId)
{
List<GradeLevels> dashboadOverAllAverage = new List<GradeLevels>();
var dashboadOverAllAverage1 = await _GpsContext.GradeLevels
.Include(d=>d.Departments)
.ThenInclude(c=>c.Courses.Where(s=>s.SchoolSystemsID ==schoolSystemId))
.ThenInclude(u=>u.Units)
.ThenInclude(l=>l.Lessons)
.ThenInclude(a=>a.Assignment)
.ThenInclude(a=>a.assignmentStudents)
.GroupBy(g=>g.ID)
.ToListAsync();
return dashboadOverAllAverage;
}
Now I want to show the data though API and want to call to fields GradeLvels name and Average Marks.
[HttpGet()]
public async Task<IActionResult> GetCEOGradeLevelAverage(string schoolSystemId)
{
var overallgradeAverages = await _ceoDashboadRepository.GetSchoolSystemGradLevelsAverage(Guid.Parse(schoolSystemId));
List<GetGradeLevelAverageVm> getOverallAverageVms = new List<GetGradeLevelAverageVm>();
foreach (GradeLevels overallgradeAverage in overallgradeAverages)
{
getOverallAverageVms.Add(new GetGradeLevelAverageVm
{
Marks = overallgradeAverage.Id.ToString(), //Want to show lable of AvrageMark
Name = overallgradeAverage.Name //Want to show Gradelevel name
});
}
return Ok(getOverallAverageVms);
}
You do select too much from your DB. Here an example, how to select the nessecary values:
using (TestDbContext ctx = new TestDbContext())
{
var tmp = ctx.AssignmentStudents
.Include(s => s.Assignment) // Include all Childs..
.ThenInclude(a => a.Lesson)
.ThenInclude(l => l.Unit)
.ThenInclude(u => u.Course)
.ThenInclude(c => c.GradeLevel)
.Where(a => a.LessonId == 123)
.GroupBy(g => // Group by your Key-Values Grade and Course (You could take names instead of ids. Just for simplification)
new
{
GradeLevel = g.Assignment.Lesson.Unit.Course.GradeLevel.Id,
Course = g.Assignment.Lesson.Unit.Course.Id
})
.Select(s => // Select the result into an anonymous type
new
{
GradeLevels = s.Key.GradeLevel, // with same keys like grouping
Course = s.Key.Course,
AverageObtainedMarks = s.Average(a => a.ObtainedMarks) // and an average ObtainedMarks from objects matching the key
})
.Where(s => s.GradeLevel == 1);
foreach (var t in tmp)
{
Console.WriteLine(t.GradeLevels + " " + t.Course + ": " + t.AverageObtainedMarks);
}
}
Here a the classes and dbcontext I used:
public class GradeLevel
{
public int Id { get; set; }
public List<Course> Courses { get; set; }
}
public class Course
{
public int Id { get; set; }
public int GradeLevelId { get; set; }
public GradeLevel GradeLevel { get; set; }
public List<Unit> Units { get; set; }
}
public class Unit
{
public int Id { get; set; }
public int CourseId { get; set; }
public Course Course { get; set; }
public List<Lesson> Lessons { get; set; }
}
public class Lesson
{
public int Id { get; set; }
public int UnitId { get; set; }
public Unit Unit { get; set; }
public List<Assignment> Assignments { get; set; }
}
public class Assignment
{
public int Id { get; set; }
public int LessonId { get; set; }
public Lesson Lesson { get; set; }
public List<AssignmentStudent> AssignmentStudents { get; set; }
}
public class AssignmentStudent
{
public int Id { get; set; }
public int AssignmentId { get; set; }
public Assignment Assignment { get; set; }
public decimal ObtainedMarks { get; set; }
}
public class TestDbContext : DbContext
{
public DbSet<AssignmentStudent> AssignmentStudents { get; set; }
public DbSet<Assignment> Assignments { get; set; }
public DbSet<Lesson> Lessons { get; set; }
public DbSet<Unit> Units { get; set; }
public DbSet<Course> Courses { get; set; }
public DbSet<GradeLevel> GradeLevels { get; set; }
}

EF: How to get "Grade" entity for "Student" having GradeId=1

I have below database structure with data,
And below are entity and dbContext classes,
public class SchoolContext : DbContext
{
public DbSet<Grade> Grades { get; set; }
public DbSet<Student> Students { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=(localdb)\MSSQLLocalDB;Database=SchoolDB;Trusted_Connection=True;");
}
}
public class Grade
{
public int Id { get; set; }
public string GradeName { get; set; }
public virtual ICollection<Student> Students { get; set; } = new HashSet<Student>();
}
public class Student
{
public int Id { get; set; }
public string StudentName { get; set; }
}
Now, I'm trying to get Grade entity for Student having GradeId=1. How to pass GradeId?
static void Main(string[] args)
{
var gradeId = 1;
var context = new SchoolContext();
var data = context.Grades.Include(x => x.Students.Where(how to pass gradeId here)).SingleOrDefault();
}
Seems like you are in fact trying to get Grade with Id equals to 1 and pre-load related data. So you can try something along these lines:
var data = context.Grades.Include(x => x.Students).SingleOrDefault(x => x.Id == 1);
One possible solution:
var data = context.Grades
.Join(context.Students.Where(s => s.GradeId == 1),
g => g.Id,
s => s.GradeId,
(g, s) => new { Id = s.Id, GradeName = g.GradeName }).ToList();

Linq multiple group by and joins

I have 3 tables in this hierarchy:
Table1 [1 to many]
--> Table2 [1 to many]
--> Table3 [many]
I would like to achieve:
var finalResult = (List<TableResult1>)linqQuery.ToList();
public class TableResult1
{
public string Key { get; set; }
public List<TableResult2> ListTableResult2 { get; set; }
}
public class TableResult2
{
public string Key { get; set; }
public List<TableResult3> ListTableResult3 { get; set; }
}
public class TableResult3
{
public string Key { get; set; }
}
Here is the models:
public class Table1
{
public int Id { get; set; }
public string SomeProperty { get; set; }
}
public class Table2
{
public int Id { get; set; }
public int Table1Id { get; set; }
public string SomeProperty { get; set; }
}
public class Table3
{
public int Id { get; set; }
public int Table2Id { get; set; }
public string SomeProperty { get; set; }
}
I can join everything then group by Table1 but that's as far as I can go.
Any help with an example would be great.
This will do it for you:
I wrote the original query against some of my entities, and then went through and renamed them to your Table1/2/3 - I might have missed something, so there may be a syntax error or two, but it should work
var table3Join =
Table2
.GroupJoin(
Table3,
ttwo => ttwo.Id,
tthree => tthree.Table2Id,
(ttwo, tthree) => new { ttwo = ttwo , tthree = tthree }
);
var sqlQuery =
Table1
.GroupJoin(
table3Join,
tone => tOne.Id,
twwo => twwo.ttwo.Table1Id,
(tone, ttwo) => new { tone = tone, ttwo = ttwo }
).ToList();
var tableResults = sqlQuery.Select(r => new TableResult1
{
Key = r.tone.Id,
ListTableResult2 = r.ttwo.Select(ttwo => new TableResult2
{
Key = ttwo.ttwo.Id,
ListTableResult3 = ttwo.tthree.Select(tthree => new TableResult3
{
Key = tthree.Id
}).ToList()
}).ToList()
});

LINQ grouping and ordering of top 5 rows into ViewModel List

I am trying to create a new list of top five hits leaders for my softball web application. I am new to MVC and I am having trouble putting this query together and passing the information to my ViewModel. I would like to use a non-query call like the "var results" in the controller if possible. I am trying to group the AtBats by PlayerID in the Stat table. I would also like to be able to call the FirstName and LastName of the player from the Player table in my view for this list. Thank you for your help!
In Stat.cs
using System;
namespace TheFlyingPig.Models
{
public class Stat
{
public int StatID { get; set; }
public int SeasonID { get; set; }
public int PlayerID { get; set; }
public DateTime GameDate { get; set; }
public int AtBats { get; set; }
public int Hits { get; set; }
public int Walks { get; set; }
public int Singles { get; set; }
public int Doubles { get; set; }
public int Triples { get; set; }
public int HomeRuns { get; set; }
public int RBIs { get; set; }
public int Runs { get; set; }
public int ReachedOnErrors { get; set; }
public int SacrificeFlies { get; set; }
public virtual Season Season { get; set; }
public virtual Player Player { get; set; }
}
}
In Player.cs
using System;
using System.Collections.Generic;
namespace TheFlyingPig.Models
{
public class Player
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<Stat> Stats { get; set; }
}
}
In Season.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace TheFlyingPig.Models
{
public class Season
{
public int SeasonID { get; set; }
public string SeasonName { get; set; }
public virtual ICollection<Stat> Stats { get; set; }
}
}
In TeamStat.cs (My ViewModel I am trying to push this data to)
//Get the season leaders from controller
public List<Stat> SeasonLeadersHits { get; set; }
I have tried both of the following options in my controller (HomeController.cs), but I just can't seem to make any headway here to get the Top 5 Hits leaders for the team.
var lastSeasonId = db.Seasons.OrderByDescending(u => u.SeasonID).Select(u => u.SeasonID).FirstOrDefault();
var leadersAtBats = (from s in db.Stats
join p in db.Players on s.PlayerID equals p.ID
where s.SeasonID == lastSeasonId
group s.Hits by s.PlayerID into g
order by s.Hits descending
select new { LeaderID = g.Key, LeaderList = g.ToList() })
.Take(5);
var results = db.Stats.Where(s => s.SeasonID == lastSeasonId).GroupBy(s => s.PlayerID, s => s.Hits, (key, g) => new { PlayerId = key, Hits = g.ToList() });
var view = new TeamStat() { SeasonLeadersHits = results };
return View(view);
What is the reason for the 'GroupBy'? It seems to me that all you need to do is select the top 5 rows from db.Stats ordering by Hits descending. Like this:
var results = (
from s in db.Stats
join p in db.Players on s.PlayerID equals p.ID
where s.SeasonID == lastSeasonId
order by s.Hits descending
select new { Player = p, Stats = s })
.Take(5).ToList();
** I'm assuming that each row in Stats represents a year.
Edit:
Ok, so each Stat object represents a single game (missed that the first time). If you want the Stats totaled by year, then the piece that is missing from your query is the Sum() of the hits (and probably the other stats as well). This query will give you the top 5 seasons ranked by total number of hits, along with the season and player associated with it.
var hitLeaders = (from s in stats
join p in players on s.PlayerID equals p.ID
group s by new { s.PlayerID, s.SeasonID } into g
select new
{
PlayerID = g.Key.PlayerID,
SeasonID = g.Key.SeasonID,
Hits = g.Sum(sum => sum.Hits),
AtBats = g.Sum(sum => sum.AtBats),
Walks = g.Sum(sum => sum.Walks),
Singles = g.Sum(sum => sum.Singles),
Doubles = g.Sum(sum => sum.Doubles),
Triples = g.Sum(sum => sum.Triples),
HomeRuns = g.Sum(sum => sum.HomeRuns),
RBIs = g.Sum(sum => sum.RBIs),
Runs = g.Sum(sum => sum.Runs),
ReachedOnErrors = g.Sum(sum => sum.ReachedOnErrors),
SacrificeFlies = g.Sum(sum => sum.SacrificeFlies)
}).OrderByDescending(s => s.Hits).Take(5);
Now, the second problem you are going to have is that you can't assign the result of this query to your view model because it produces an IEnumerable<of AnonymousType> but the view model is expecting a List<Stat>. What I would suggest is to create an object that represents a total of the stats in a year for a player; something like this:
public class TotalStat
{
public int TotalStatID { get; set; }
public int SeasonID { get; set; }
public int PlayerID { get; set; }
public int AtBats { get; set; }
public int Hits { get; set; }
public int Walks { get; set; }
public int Singles { get; set; }
public int Doubles { get; set; }
public int Triples { get; set; }
public int HomeRuns { get; set; }
public int RBIs { get; set; }
public int Runs { get; set; }
public int ReachedOnErrors { get; set; }
public int SacrificeFlies { get; set; }
}
Then, you'll just need to change the view model to:
public List<TotalStat> SeasonLeadersHits { get; set; }
And change the query to create an IEnumerable<TotalStat>. You would achieve that by changing new {} to new TotalStat {}, like so:
//...
group s by new { s.PlayerID, s.SeasonID } into g
select new TotalStat
{
PlayerID = g.Key.PlayerID,
//...

Categories

Resources