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; }
}
Related
I am creating a dashboard in ASP.NET MVC using C# and Entity Framework.
I have two tables
Tbl_Channel (Id, Channel_Name)
Tbl_News (Id, Channel_Id, News_Title, News_Description)
When I tried this query:
public ActionResult Dashboard()
{
ShowData model = new ShowData();
var rs1 = (from c in db.Tbl_Channel select c).ToList();
var rs2 = (from c in db.Tbl_News
join d in db.Tbl_Channel on c.Channel_Id equals d.Id
select new
{
c.Id,
c.News_Title,
c.News_Description,
d.Channel_Name
})
.OrderByDescending(x => x.Id)
.ToList();
model.tbl_ChannelData = rs1;
model.tbl_NewsData = rs2;
return View(model);
}
I get the following error in line model.tbl_NewsData = rs2:
Model class :
public class ShowData
{
public List<Tbl_Channel> tbl_ChannelData { get; set; }
public List<Tbl_News> tbl_NewsData { get; set; }
}
Error:
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'System.Collections.Generic.List
The output I want to display be like:
(Id, Channel_Name, News_Title, News_Description)
The root cause of the problem is this: you have defined your properties in the model class like this:
public class ShowData
{
public List<Tbl_Channel> tbl_ChannelData { get; set; }
public List<Tbl_News> tbl_NewsData { get; set; }
}
Yet, your query selects an anonymous type:
select new
{
c.Id,
c.News_Title,
c.News_Description,
d.Channel_Name
}
This causes the error. What you need to do is instantiate objects of the type defined on your model class, e.g.
select new Tbl_News
{
c.Id,
c.News_Title,
c.News_Description,
d.Channel_Name
}
or if this anonymous type doesn't match any existing type yet, you need to define a suitable type
public class ChannelAndNews
{
public int ChannelId { get; set; }
public string NewsTitle { get; set; }
public string NewsDescription { get; set; }
public string ChannelName { get; set; }
}
and create instances of that type, which you can then assign to the model class properties:
select new ChannelAndNews
{
ChannelId = c.Id,
NewsTitle = c.News_Title,
NewsDescription = c.News_Description,
ChannelName = d.Channel_Name
}
Changing query to this one maybe it work:
var query = (from c in db.tblnews
join d in db.tblchannel on c.Channel_Id equals d.Id
Select new Data()
{
Id = c.Id,
News_Title= c.News_Title,
News_Description= c.News_Description,
Channel_Name= d.Channel_Name
})
.OrderByDescending(x => x.Id)
.ToList();
return View(query)
create this class:
public class Data
{
public int Id{ get; set; }
public string News_Title{ get; set; }
public string News_Description{ get; set; }
public string Channel_Name{ get; set; }
}
Your Expected Result:[Just Assumption]
[]1
The Classes Needed:[In Your Case]
public class Tbl_Channel
{
public int Id { get; set; }
public string Channel_Name { get; set; }
}
public class Tbl_News
{
public int Id { get; set; }
public int Channel_Id { get; set; }
public string News_Title { get; set; }
public string News_Description { get; set; }
}
public class ChannelsAndNews
{
public int ChannelId { get; set; }
public string Channel_Name { get; set; }
public string News_Title { get; set; }
public string News_Description { get; set; }
}
public class ShowData
{
public List<Tbl_Channel> Channels { get; set; }
public List<Tbl_News> News { get; set; }
public List<ChannelsAndNews> ChannelsAndNews { get; set; }
}
The Controller Method: [Changes]
public ActionResult ShowData()
{
// GetChannels() & GetNews() -- Seeded Locally in your case from DB.
var showDataModel = new ShowData();
showDataModel.Channels = (from channel in GetChannels() select channel).ToList();
showDataModel.News = (from news in GetNews() select news).ToList();
showDataModel.ChannelsAndNews = (from channel in GetChannels()
join news in GetNews() on channel.Id equals news.Channel_Id
select new ChannelsAndNews
{
ChannelId = channel.Id,
Channel_Name = channel.Channel_Name,
News_Title = news.News_Title,
News_Description = news.News_Description
})
.OrderByDescending(channelnews => channelnews.ChannelId)
.ToList();
return View(showDataModel);
}
This just seems to be working fine.
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();
}
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()
});
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,
//...
I have a list of information that I keep in the session. Want a LINQ to join this this list and a table of the database.
The result is coming reset, nothing. What am I doing wrong?
Class of List:
public class ASOProcedimentoTemp
{
public int FuncionarioID { get; set; }
public int ProcedimentoID { get; set; }
}
Class of table database:
public class ASOProcedimento : IEntidadeBase
{
[Key]
public int ASOProcedimentoID { get; set; }
public int ASOID { get; set; }
[ForeignKey("ASOID")]
public virtual ASO ASO { get; set; }
public int ProcedimentoID { get; set; }
[ForeignKey("ProcedimentoID")]
public virtual Procedimento Procedimento { get; set; }
public DateTime DtProcedimento { get; set; }
}
My LINQ:
var dadosTemp = HttpContext.Current.Session["ASOProcedimentoTemp"] as List<ASOProcedimentoTemp>;
if (dadosTemp == null)
dadosTemp = new List<ASOProcedimentoTemp>();
result =
(from a in dados.AsQueryable()
join b in dadosTemp on a.ProcedimentoID equals b.ProcedimentoID
select new
{
a.ProcedimentoID,
a.Procedimento.Descricao
})
.Where(where, filtro)
.OrderBy(orderna + " " + ordenaTipo);
The Contains will cause a SQL Where In clause with your IDs in your sql call.
var dadosTemp = HttpContext.Current.Session["ASOProcedimentoTemp"] as List<ASOProcedimentoTemp>;
if (dadosTemp == null)
dadosTemp = new List<ASOProcedimentoTemp>();
var procedimentoIDs = dadosTemp.Select(a => a.ProcedimentoID).ToList();
result =
(from a in dados
where procedimentoIDs.Contains(a.ProcedimentoID)
select new
{
a.ProcedimentoID,
a.Procedimento.Descricao
});