Linq multiple group by and joins - c#

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

Related

Nhibernate queryover (or lambda Linq) to add count of subquery in projection

given this table
TABLE A(
IdA NUMERIC PRIMARY KEY
,DescA VARCHAR2(200)
)
TABLE B(
IdB NUMERIC PRIMARY KEY
,IdA NUMERIC
,DescB VARCHAR2(200)
)
i want to select
IdA
, DescA
, Count Of B For Each A
into custom DTO
projection with is dto is not a problem but i'am looking a way to add the count to my query
A AAlias = null;
DtoType myDTO = null;
return _session.QueryOver<A>(() => AAlias )
.Select(Projections.Property(() => AAlias.IdA).WithAlias(() => myDTO.IdA),
Projections.Property(() => corsoAlias.DescA).WithAlias(() => myDTO.DescA))
.TransformUsing(Transformers.AliasToBean<DtoType>())
.List<DtoType>()
.AsEnumerable();
EDIT:
domain object:
public class TableA
{
public Guid Id { get; set; }
public string Desc { get; set; }
public ICollection<B> Bs { get; set; }
}
public class TableB
{
public Guid Id { get; set; }
public string Desc { get; set; }
}
As you say, you want to count all TableB for every TableA. You will need a subquery for this.
public class TableA
{
public Guid Id { get; set; }
public string Desc { get; set; }
}
public class TableB
{
public Guid Id { get; set; }
public TableA TableA { get; set; }
public string Desc { get; set; }
}
public class Dto
{
public Guid IdA { get; set; }
public string DescA { get; set; }
public int CountOfBForEachA { get; set; }
}
public class MyDtoService : IMyDtoService
{
private readonly ISession _session;
public MyDtoService(ISession session)
{
_session = session;
}
public void GetDto()
{
TableA tableA = null;
TableB tableB = null;
Dto dto = null;
var countOfBForEachASubQuery = QueryOver.Of(() => tableB)
.Where(() => tableB.TableA.Id == tableA.Id)
.Select(x => tableB.Id);
var result = _session.QueryOver(() => tableA)
.Select(Projections.Property(() => tableA.Id),
Projections.Property(() => tableA.Desc).WithAlias(() => dto.DescA),
Projections.SubQuery(countOfBForEachASubQuery.DetachedCriteria).WithAlias(() => dto.CountOfBForEachA))
.TransformUsing(Transformers.AliasToBean<Dto>())
.List<Dto>();
}
}

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 to display two tables separately in a single view with a join query in one table?

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.

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

How to join two tables with linq?

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

Categories

Resources