Linq with conditional joins - c#

I couldn't convert the following left join SQL to linq:
select Students.StudentID, StudentAddresses.state
from Students left join Studentaddresses on (Students.StudentID = Studentaddresses.StudentID and StudentAddresses.Active=1)
where (StudentAddresses.Rank =1 or StudentAddresses.StudentID is null)
and Students.StudentID =3
A student can have zero record or multiple records in the Student Address table, but only one of the records can be active and rank=1.
I was able to do a left join in linq and make it work for normal situation. But if a student has two inactive records in the Student table, I don't know how to make the student record appear only once in the final result. Can anyone please help?

Use Distinct() to collapse duplicates.
var LeftJoin = (from student in Students
join address in (from address1 in StudentAddresses where address.Active select address1)
on student.StudentID equals address.StudentId
into JoinedStudentAddress
from joined in JoinedStudentAddress.DefaultIfEmpty()
select new
{
StudentID = student.StudentID,
State = joined != null ? joined.State : null
}).Distinct();
Alternate syntax
var LeftJoin = Students.GroupJoin( StudentAddress.Where( a => a.Active ),
s => s.StudentID,
a => a.StudentID,
(s,a) => new { Student = s, Addresses = a } )
.SelectMany( j = > j.Addresses.DefaultIfEmpty()
(s,a) => new {
StudentID = s.Student.StudentID,
State = a != null ? a.State : null
})
.Distinct();

This would be the SQL statement converted to linq:
var q = from student in Students
from adress in Studentaddresses.Where(x => student.StudentID == x.StudentID && x.Active).DefaultIfEmpty()
where (adress.Rank == 1 || adress.StudentID == null) && student.StudentID == 3
select new
{
StudentID = student.StudentID,
State = adress.state
};

Related

MVC 5 Linq-SQL Performing search from 3 joined Tables

Using this model, I would like to perform a left outer join of the course table to the student table and display them with a search dropdown menu on the courses.
My MVC code is :
var query = from c in db.Students
join o in db.Enrollments on c.StudentID equals o.StudentID
join co in db.Courses on o.CourseID equals co.CourseID into sr
from x in sr.DefaultIfEmpty()
select new Student
{
FirstName=c.FirstName,
LastName=c.LastName,
EnrollmentDate=c.EnrollmentDate,
MiddleName=c.MiddleName,
StudentID=c.StudentID
//StudentName = c.FirstName.ToString(),
//CourseID = x.CourseID.ToString(),
//CourseName = x.Title.ToString()
//== null ? -1 : x.Title
};
if (!string.IsNullOrEmpty(course))
{
students = query.Where(x => x.CourseName == course).Select(item=>new Student(){FirstName = c.FirstName.ToString()}).ToList();
}
return View(students);
But I can't get it to work. Can someone please enlighten me on how to correctly do this.
This is the sample screen :
public List<Student> GetStudentsByCourseName(string courseName)
{
var list = new List<Student>();
var course = db.Courses.SingleOrDefault(o => o.Title == courseName);
if (course != null)
{
list = course.Enrollments.Select(o => new Student {
FirstName = o.Student.FirstName,
LastName = o.Student.LastName
}).ToList();
}
return list;
}

left join selector can't access single left table name

I am coding my way through the 101 linq examples, and I'm now at #106.
I am trying to rewrite query expressions in method/lambda syntax for my own learning.
Here is the example code:
List<Customer> customers = GetCustomerList();
List<Supplier> suppliers = GetSupplierList();
var custSuppliers =
from cust in customers
join sup in suppliers on cust.Country equals sup.Country into ss
from s in ss.DefaultIfEmpty()
orderby cust.CompanyName
select new
{
Country = cust.Country,
CompanyName = cust.CompanyName,
SupplierName = s == null ? "(No suppliers)" : s.SupplierName
};
Here is what I have so far:
var custSuppliers =
customers.GroupJoin(suppliers, c => c.Country, s => s.Country, (c, s) => new { Customers = customers, Suppliers = suppliers })
.OrderBy(i => i.Customers)
.SelectMany(x => x.Suppliers.DefaultIfEmpty(), (x, p) => // p is the many field (i.e. customers)
new
{
CompanyName = x.CompanyName, // no definition for CompanyName
Country = p.Country,
SupplierName = p.SupplierName == null ? "(No suppliers)" : p.SupplierName
});
My understanding is the SelectMany takes parameters X and P where X is the "left" table (i.e. there is at most 1 of them) and P is the "right" table where there might be N of them (or a null).
But instead of the X variable holding a single customer, it holds a collection of suppliers and customers.
Can anyone explain what is going on here?

Entity Framework - Handle null value in Linq

I'm writing two LINQ queries where I use my first query's result set in my second query.
But in some cases when there is no data in the database table my first query returns null,
and because of this my second query fails since wsdetails.location and wsdetails.worklocation are null causing an exception.
Exception:
Object reference not set to an instance of an object
My code is this:
var wsdetails = (from assetTable in Repository.Asset
join userAsset in Repository.UserAsset on
assetTable.Asset_Id equals userAsset.Asset_Id
join subLocationTable in Repository.SubLocation on
assetTable.Sub_Location_Id equals subLocationTable.Sub_Location_ID
where userAsset.User_Id == userCode
&& assetTable.Asset_TypeId == 1 && assetTable.Asset_SubType_Id == 1
select new { workstation = subLocationTable.Sub_Location_Name, location = assetTable.Location_Id }).FirstOrDefault();
result = (from emp in this.Repository.Employee
join designation in this.Repository.Designation on
emp.DesignationId equals designation.Id
where emp.Code == userCode
select new EmployeeDetails
{
firstname = emp.FirstName,
lastname = emp.LastName,
designation = designation.Title,
LocationId = wsdetails.location,
WorkStationName = wsdetails.workstation
}).SingleOrDefault();
As a workaround I can check
if wsdetails == null
and change my second LINQ logic, but I believe there are some ways to handle null values in LINQ itself like the ?? operator.
But I tried this and it didn't work for me.
Any help?
The problem is EF can't translate the null-coalescing operator to SQL. Personally I don't see what's wrong with checking the result with an if statement before executing the next query. However, if you don't want to do that, then because your result is always going to be a single query why not do something like:
var wsdetails = (from assetTable in Repository.Asset
join userAsset in Repository.UserAsset on
assetTable.Asset_Id equals userAsset.Asset_Id
join subLocationTable in Repository.SubLocation on
assetTable.Sub_Location_Id equals subLocationTable.Sub_Location_ID
where userAsset.User_Id == userCode
&& assetTable.Asset_TypeId == 1 && assetTable.Asset_SubType_Id == 1
select new { workstation = subLocationTable.Sub_Location_Name, location = assetTable.Location_Id }).FirstOrDefault();
result = (from emp in this.Repository.Employee
join designation in this.Repository.Designation on
emp.DesignationId equals designation.Id
where emp.Code == userCode
select new EmployeeDetails
{
firstname = emp.FirstName,
lastname = emp.LastName,
designation = designation.Title
}).SingleOrDefault();
result.LocationId = wsdetails != null ? wsdetails.location : "someDefaultValue";
result.WorkStationName = wsdetails != null ? wsdetails.workstation ?? "someDefaultValue";
Instead of the "binary" operator ?? maybe you should use the good old ternary one, ? :, like in
wsdetails != null ? wsdetails.location : null
Try not evaluating the first query, and use it in the second query. This should result in a single SQL statement.
var wsdetails = (from assetTable in Repository.Asset
join userAsset in Repository.UserAsset on
assetTable.Asset_Id equals userAsset.Asset_Id
join subLocationTable in Repository.SubLocation on
assetTable.Sub_Location_Id equals subLocationTable.Sub_Location_ID
where userAsset.User_Id == userCode
&& assetTable.Asset_TypeId == 1 && assetTable.Asset_SubType_Id == 1
select new { workstation = subLocationTable.Sub_Location_Name, location = assetTable.Location_Id });
// wsdetails is still an IEnumerable/IQueryable
result = (from emp in this.Repository.Employee
join designation in this.Repository.Designation on
emp.DesignationId equals designation.Id
where emp.Code == userCode
select new EmployeeDetails
{
firstname = emp.FirstName,
lastname = emp.LastName,
designation = designation.Title,
LocationId = wsdetails.First().location,
WorkStationName = wsdetails.First().workstation
}).SingleOrDefault();

self referential join using linq

My database has a students table:
StudentID int PK
StudentName varchar
FamilyID int (Nullable)
If a student has siblings in the database, they will share a familyID. If a student has no siblings, familyID should be null.
Using Linq, given a StudentID, how do a return a list containing a student and all his siblings (if any)?
A join with an into should do the trick.
var query = from s in students
join s2 in students.Where(x => x.StudentId != s.StudentId) on s.FamilyId equals s2.FamilyId into siblings
select new
{
Student = s,
Siblings = siblings,
};
You can add a query.FirstOrDefault(x => s.StudentId == yourStudentId) in order to get a specific StudentId
Using a LinQ method chain, this will return a list of only the siblings:
var siblings = students.Where(s => s.FamilyId == students.Single(st => st.StudentId == id).FamilyId).ToList();
or in 2 steps so you could provide checks on the student-instance:
var student = students.Single(st => st.Id == id);
var siblings = students.Where(s => s.FamilyId == student.FamilyId).ToList();
You can do something like this
List<Student> lstStd = new List<Student>();
var student = (from s in students
where s.studentid = studentid
select s).FirstOrDefault();
lstStd.Add(student);
if(student!=null)
{
if( student.FamilyId !=null)
{
lstStd.AddRange( (from s in students
where s.FamilyId == student.Familyid
&& s.studentid != student.studentid
select s).ToList<Student>());
}
}
var sid = 123;
var query = from s in Students
where (from s2 in Students where s2.Id == sid select s2.FamilyId).FirstOrDefault() == s.FamilyId
&& s.FamilyId != null
select s;
if i understand your question correctly. you are looking for the family members of a specified student id. this answer is based on the assumption that you are only looking for one family at a time, and not a list of all families.

Need help with converting SQL to LINQ - LEFT JOIN with Count

I need help converting the following SQL query into LINQ:
select s.teacherid,t.lastname,t.firstname,t.title,t.grade, count(s.TeacherID)
from Teacher t
left join Student s on t.teacherid = s.teacherid
and t.isactive = 1
and s.isactive = 1
group by s.TeacherID,t.lastname,t.firstname,t.title,t.grade
I tried the following but it is not returning teacher records who have no students:
var teachers =
(from t in Teachers
join s in Students on t.TeacherID equals s.TeacherID
into results
where t.IsActive == true
from r in results
group r by new { r.TeacherID, r.Teacher.FirstName, r.Teacher.LastName, r.Teacher.Title, r.Teacher.Grade} into g
select new { TeacherID = g.Key.TeacherID,FirstName = g.Key.FirstName, LastName = g.Key.LastName, Title=g.Key.Title, Grade = g.Key.Grade}
);
Thank you in advance!
In order to do a left join you will need to use DefaultIfEmpty() method like so:
from r in results.DefaultIfEmpty()
var teachers =
(from t in Teachers
join s in Students on t.TeacherID equals s.TeacherID
into results
where t.IsActive == true
from r in results.DefaultIfEmpty()
group r by new { r.TeacherID, r.Teacher.FirstName, r.Teacher.LastName, r.Teacher.Title, r.Teacher.Grade} into g
select new { TeacherID = g.Key.TeacherID,FirstName = g.Key.FirstName, LastName = g.Key.LastName, Title=g.Key.Title, Grade = g.Key.Grade}
);
I did some research and found a solution. The following expression fixed it:
var results = Teachers
.Where(t => t.IsActive == true)
.Select(t =>
{
TeacherID = t.TeacherID,
FirstName = t.FirstName,
LastName = t.LastName,
Title = t.Title,
Grade = t.Grade,
Count = t.Students.Where(s => s.IsActive == true).Count()
});
results.ToList().Dump();

Categories