Good Afternoon! Could anyone suggest how I can implement autoincrement in linq to object subquery which must be started from 1 on every group of main linq query? I have query
from student in students
group student by student
into keyStudents
select new StudentClass
{
Student = keyStudent.Key,
Subjects = from key in keyStudent
select new SubjectClass
{
Subject = key.Subject,
SubjectId = (stuck here)
}
}
My result must be like this:
Student
John
1. Math
2. Bio
Fred
1. Math
2. Physics
3. Bio
My query returns accurate data without SubjectId, but I need to show SubjectId as well. So the SubjectId is just index number started with one in every group.
You'd have to use method syntax so you can use the override of Select that includes an index
from student in students
group student by student
into keyStudents
select new StudentClass
{
Student = keyStudent.Key,
Subjects = keyStudent.Select((x, i) =>
new SubjectClass
{
Subject = x.Subject,
SubjectId = i + 1,
}),
}
Related
I have one table Texting, that I need to join with another two tables Student and Staff to search for information about these 2 tables.
Student fields:
Id
Name
... and a bunch of other fields specific to student
Staff fields:
Id
Name
... and a bunch of other fields specific to staff
Texting fields:
Id
PersonId // contains either student ID or staff ID
PersonTypeId // indicates whether PersonId is of type student or staff (student = 1, staff = 2)
Now I need to write a linq query to search the table Texting either by student or staff name but I am stuck on the linq to achieve this.
var query = (from t in texting
join s in studentBo.GetListQuery()
on t.PersonId equals s.Id
join st in staffBo.GetListQuery()
on t.PersonId equals st.Id
where ...
select t);
This joins the tables together but it doesnt care what the PersonId type is so it's all mixed. How do i specify so that it joins the PersonId correctly according to the right PersonTypeId? It seems like nothing else can be appended on the on clause or where clause to make this happen = (.
So you have a name, and you want all Textings that refer to a Student with this name, and all Textings that refer to a member of Staff with this Name.
My advice would be to concat the Student textings with the Staff textings. You could do that in one big LINQ statements, however this would make it quite difficult to understand. So I'll do it in two steps, then Concat it in one query:
const int student = 1;
string name = "William Shakespeare";
var studentTextings = textings.Where(texting => texting.PersonTypeId == student)
.Join(students.Where(student => student.Name == name),
texting => texting.PersonId, // from every Texting take the foreign key
student => student.Id, // from every Student take the primary key
// parameter resultSelector:
// from every texting with its matching student make one new:
(texting, studentWithThisTexting) => new
{
// Select the Texting properties that you plan to use
Id = texting.Id,
...
}
In words: from all Textings, keep only those Textings that refer to a student, so you know that the foreign key refers to a primary key in the table of Students. From all Students keep only those Students that have the requested name.
Join all remaining Textings and the few remaining Students that have this name on primary and matching foreign key.
Do something similar for members of Staff:
const int staff = 2;
var staffTextings = textings.Where(texting => texting.PersonTypeId == staff)
.Join(staffMembers.Where(staffMember => staffMember.Name == name),
texting => texting.PersonId, // from every Texting take the foreign key
staffMember => staffMember.Id, // from every Staff member take the primary key
// parameter resultSelector:
(texting, staffMembers) => new
{
// Select the Texting properties that you plan to use
Id = texting.Id,
...
}
Now all you have to do is Concat these two. Be aware: you can only Concat similar items, so the resultSelector in both Joins should select objects of exactly the same type.
var textingsOfPersonsWithThisName = studentTextings.Concat(staffTextings);
There is room for improvement!
If you look closely, you'll see that the textings table will be scanned twice. The reason for this, is because your database is not normalized.
Can it be, that a Texting for a Student will ever become a Texting for a member of Staff? If not, my advice would be to make two tables: StudentTextings and StaffTextings. Apart from that queries will be faster, because you don't have to check PersonType, this also has the advantage that if later you decide that a StudentTexting differs from a StaffTexting, you can change the tables without running into problems.
If you really think that sometimes you need to change the type of a texting, and you don't want to do this by creating a new texting, you also should have two tables: one with StudentTextings, and one with StaffTextings, both tables having a one-to-one relations with a Texting.
So Students have one-to-many with StudentTextings, which have one-to-one with Textings. Similar for Staff and StaffTextings.
So Student [4] has 3 StudentTextings with Id [30], [34], [37]. Each of these StudentTextings have a foreign key StudentId with value [4]. Each StudentTexting refers to their own Texting with a foreign key: [30] refers to texting [101], so it has foreign key 101, etc.
Now if texting [101] has to become a texting for Staff [7], you'll have to delete the StudentTexting that refers to [101] and create a new StaffTexting that refers to Staff [7] and Texting [101]
By the way, since the combination [StudentId, TextingId] will be unique, table StudentTextings can use this combination as primary key. Similar for StaffTextings
You will have to merge Student and Staff tables, otherwise all your queries will be too complicated, since you will have to use Union
Person
Id
Name
PersonType
Texting
Id
PersonId
and query
var query = (from t in texting
join p in person
on t.PersonId equals p.Id
where ...
select t);
PS if you still want a query with 2 tables instead of one, you will have to post the real code.
You'll need to do these as two separate queries, project to a new type and then union the results. Messy, but here's how.
First get your students:
var textingStudents = (
from s in students
join t in texting on s.Id equals t.PersonId
where t.PersonTypeId == 1
select new { id = s.Id, personTypeId = 1, name = s.Name }).ToList();
Now get your staff in almost the exact same way:
var textingStaff = (
from s in staff
join t in texting on s.Id equals t.PersonId
where t.PersonTypeId == 2
select new { id = s.Id, personTypeId = 2, name = s.Name }).ToList();
Now you can union the two:
var allTextingPeople = textingStudents.Union(textingStaff);
If you need additional properties then add then to the anonymous type declared in the select statement - remember, the type will need to have the same properties in both the textingStudents and textingStaff result. Alternatively, define a class and do a select new MyUnionClass { ... } in both queries.
Edit
You're going to probably get into a world of hurt with the current approach you've outlined. If you're using a relational database (i.e. sql server) you almost certainly are not defining constraints such as foreign keys on your Texting table meaning you'll end up with ID clashes and will definitely end up with bugs later down the road. Best approach is probably to have one table to represent Staff and Student (let's call it Person with a column defining the "type" of person - the column itself will be foreign key link to another table with your list of PersonTypes
I am using SQL Server 2017, .Net 4.5 and EF core.
I have the following tables: Student_Course: has Student_Id, Course_ID and Student
_Course_ID - pk.
Student: First_Name, Last_Name, Email, Student_ID - PK
Course: Id pk, Name
I am trying to retrieve a list of students with their respective
courses. I also need the course count for each student.
For the above mentioned tables I have the following entities in C#
Student_Course: Has navigational properties for list of students and courses
Student:
Course:
I am trying to make this query using LINQ:
Controller:
public IList<StudentCourse> GetStudents()
{
Student_CourseContext scContext = new Student_CourseContext();
var studentCourses = (from sc in scContext.Student_Course
from student in sc.Students.Where( x => x.Student_ID ==
sc.Student_ID)
from course in sc.Courses.Where (x => x.ID == sc.Course_ID)
where sc.Student_ID == student.Student_ID
&& sc.Course_ID == course.ID
&& student.First_Name != "None"
//join course in sc.Courses
//from sc in studentCourseEntity.Student_Courses.GroupBy( x => x.Student_ID)
select new StudentCourse
{
StudentName = student.First_Name + ' ' + student.Last_Name,
//CourseCount = sc.Gr,
Course = string.Join(",", course.Name)
}).ToList();
return studentCourses.ToList();
}
}
It is returning a query like this:
SELECT ([s0].[First_Name] + CAST(N' ' AS nvarchar(max))) + [s0].[Last_Name], [c].[Name]
FROM [Student_Course] AS [s]
INNER JOIN [Student] AS [s0] ON ([s].[Student_Course_ID] = [s0].[Student_Course_ID]) AND ([s].[Student_ID] = [s0].[Student_ID])
INNER JOIN [Course] AS [c] ON ([s].[Student_Course_ID] = [c].[Student_Course_ID]) AND ([s].[Course_ID] = [c].[ID])
WHERE (([s].[Student_ID] = [s0].[Student_ID]) AND ([s].[Course_ID] = [c].[ID])) AND (([s0].[First_Name] <> N'None') OR [s0].[First_Name] IS NULL)
Instead of joining on the columns pecfied it is joining on STudent_Course_ID which is the primary key of the Student_Course table. Due to this join I am getting the error:
Invalid column name 'Student_Course_ID'. Invalid column name 'Student_Course_ID'.
since the Student and Course tables do not have this field. Please let
me know how I can make this work.
I tried mode.OnCreating method to define the relationship betweeen
these tables but I am able to map between Student, Student_Course and
Course tables.
Thanks in advance.
So you want for every Student his name and the list (collection?) of the names of all courses he attends.
var studentsWithTheCoursesTheyAttend = dbContext.Students
.Select(student => new
{
Name = student.First_Name + ' ' + student.Last_Name,
Courses = dbContext.Student_Course
.Where(studentCourse => studentCourse.StudentId == student.Id)
.Select(studentCourse => dbContext.Courses
.Where(course => course.Id == studentCourse.CourseId)
.Select(course => course.Name)
.FirstOrDefault())
.ToList(),
}
Explanatation: from every Student in the complete table of Students, take his name:
... = dbContext.Students
.Select(student => new
{
Name = student.First_Name + ' ' + student.Last_Name,
Also select all the Student_Courses of this Student (= all Student_Courses that have a foreign key StudentId that equals the primary key of the Student):
Courses = dbContext.Student_Course
.Where(studentCourse => studentCourse.StudentId == student.Id)
From every selected Student_Course of this Student you want to get the Name of the Course that this Student_Course refers to. For this we will first get all Courses with a primary key equal to the foreign key StudentCourse.CourseId:
.Select(studentCourse => dbContext.Courses
.Where(course => course.Id == studentCourse.CourseId)
Well, we don't want the complet course, we only want its Name:
.Select(course => course.Name)
Well, we know that every studentCourse will only have exactly one Course that it refers to, not zero, not more than one. After all, the foreign key studentCourse.CourseId refers to exactly one Course.
Therefore we only have to take the first one. I could have used First(), but some Providers in the IQueryable report problems when using First, hence I use FirstOrDefault().
.FirstOrDefault(),
So by now, from ever Student, I have his Name, and the Names of all Courses he attends. Make this a List of courses:
Courses = ...
.ToList(),
So now you have something like this:
{
Name = "Sukumar Krishnamurthy",
Courses =
{
"Advanced .NET",
"All aspects of LINQ",
"Ups and downs of C#",
},
},
{
Name = "Harald Coppoolse",
Courses =
{
"Windows Services",
"REST in a proper manner",
},
},
Property Courses is the List of Names of Courses that the Student attends. The length of the List represents the number of attended Courses.
If desired you can transform this list of Courses into one string with Names. However I am not sure that this will improve readability:
"Windows Services REST in a proper manner"
I've a Many to Many relation between a student (etudiant) and a course (cours) table which is called studentCours in SQL but not use as a model in Entity Framework (i've taken the habit of it now, even if i think its weird).
I need to show the result of the student (which is on another table etuResult) but the student may not have any result (nullable int) yet as the year is still in progress !
I'm able to get the value of studentCours and show them and also of studentResult, but i'm not able to show the concatenation of both (and leave the result empty or with other value than a number between 1 and 20).
On the following picture you can see :
On the left : the student with the id 3 has 2 cours with the id 1 and 10 (i dont know why the course_id 1 appears twice...) => These are the course he's following.
On the right : The student with the id 3 has one result for the course_id 10 and has the result 10/20
Here are my two queries to get the data :
var etuCours = from c in bdd.cours
where c.etudiant.Any(e => e.etudiant_id == id)
select new resultCours { cours_id = c.cours_id, libelle = c.libelle, code = c.code, annee = c.annee };
IEnumerable<resultCours> etuResult = from er in bdd.etuResult
where er.etudiant_id == id
select new resultCours { cours_id = er.cours_id, cote = er.cote };
I'm stuck trying to come up with Linq for a particular case. I don't want to use the names of my client's database table so I've translated the situation to your typical classroom setting.
One classroom has multiple students, or even no students.
Each class may or may not be assigned a grade average. Don't worry about where the grade comes from or why it's serparate from the class.
Using Linq, I want to get all the classrooms that have more than 5 students AND have been assigned the grade average.
The objects look like this.
class Classrooms {
Guid ClassroomId;
string subject;
}
class Students {
Guid Student;
Guid ClassroomId;
}
class Score {
Guid ClassroomId;
int someScoreHere;
}
In SQL I look for something like this:
SELECT COUNT(*) As NumberOfStudents, ClassroomId
FROM Students WHERE ClassroomId IN (SELECT ClassroomId FROM Score)
GROUP BY ClassroomId HAVING COUNT(StudentId) > 5 BY NumberOfStudents;
My fail Linq attempt at getting a list of the Classrooms;
Guid[] scores = (from score in db.Score select score.ClassroomId).ToArray();
List<Classroom> result = (from c in db.Classroom
where db.Student.Contains(c.ClassroomId))
group c by c.ClassroomId.... BLEEEH
from c in db.Classroom
where db.Score.Select(s => s.ClassroomId).Contains(c.ClassroomId)
group c by c.ClassroomId into g
where g.Count() > 5
select new { NumberOfStudents = g.Count(), ClassroomId = g.Key }
I have the following query:
var rowData = companies.Select(
t => new CompanyDetail
{
CompanyID = t.Title,
Subjects = subjects.Count ( u => t.RowKey == "0000" + u.PartitionKey)
}).ToList();
public class CompanyDetail
{
[DisplayName("Company")]
public string CompanyID { get; set; }
[DisplayName("Subjects")]
public Int32 Subjects { get; set; }
}
The query output looks like this:
CompanyID Subjects
1 2
2 4
3 1
However I have a problem if the company has no subjects. I would like to see:
CompanyID Subjects
1 2
2 4
3 1
4 0
Is there a way that I can convert this LINQ query into an outer join so it always reports every company and then gives a count of how many subjects are connected through the row and partitionkey connector?
It's hard to answer this without knowing what LINQ provider you are using - your query would work as you expect in LINQ to Objects, for example.
Perhaps it would be worth a try to use an explicit GroupJoin to convince the provider to give you empty groups:
var rowData = from company in companies
join subject in subjects
on company.RowKey equals "0000" + subject.PartitionKey
into companySubjectGroup
select new
{
CompanyID = company.Title,
Subjects = companySubjectGroup.Count()
};