LINQ - nested GroupJoin - c#

I have looked at many LINQ examples on how to do GroupJoin. But I am not sure how to perform nested GroupJoin. Does somebody have any idea?
I have a following simple classes:
public class Subject
{
public int SubjectID { get; set;}
public string Name { get; set; }
}
public class SubjectStudent
{
public int SubjectStudentID { get; set; }
public int SubjectID { get; set; }
public int StudentID { get; set; }
}
public class StudentGrade
{
public int StudentGradeID { get; set;}
public int StudentID { get; set; }
public int GradeID { get; set; }
}
var subjects = (from s in Subject
join ss in SubjectStudent on s.SubjectID equals ss.SubjectID into SubjectStudents
select new
{
SubjectID = s.SubjectID,
Students = SubjectStudents
})
.ToList();
foreach (var subject in subjects)
{
foreach(var student in subject.Students)
{
//foreach(var grade in student.Grades)
//{
// I want to get grades for each subject.Students
//}
}
}
Can I have another GroupJoin after SubjectStudents, i.e. StudentGrades? I want to be able to iterate over StudentGrades in each subject.Students.
Thank you for any help.

Your data structure looks a bit confusing to me. Also, not sue if this is what you expect:-
var result = (from s in subjects
join ss in subjectStudents on s.SubjectID equals ss.SubjectID into SubjectStudents
select new
{
SubjectID = s.SubjectID,
Students = from ss in SubjectStudents
join g in studentsGrade on ss.StudentID equals g.StudentID
select new
{
StudentId = ss.StudentID,
GradeId = g.GradeID
}
})
.ToList();
Sample Fiddle

Related

How to write this code using the Linq Extension Method-Syntax?

I have these following classes from an Linq Example:
public class Customer
{
public Customer();
public Cities City { get; set; }
public string Name { get; set; }
public Order[] Orders { get; set; }
}
public class Product
{
public Product();
public double Price { get; set; }
public int ProductID { get; set; }
public string ProductName { get; set; }
}
public class Order
{
public Order();
public int OrderID { get; set; }
public int ProductID { get; set; }
public int Quantity { get; set; }
public bool Shipped { get; set; }
}
static void Main(string[] args)
{
var allOrders = from cust in customers
from ord in cust.Orders
join prod in products on ord.ProductID equals prod.ProductID
select new
{
cust.Name,
ord.ProductID,
OrderAmount = ord.Quantity * prod.Price
};
}
I want to create the same collection (Name, ProductID, Orderamount) with the linq Extension Method Syntax. My problem is that I don't know how to realize the two datasources from cust in customers from ord in cust.Orders in the Extension Method Syntax.
Does anyone have any idea how it could work?
I got this but I have no access to the `CustomerName in the collection.
var allOrders2 =
customers.SelectMany(cust => cust.Orders)
.Join(products,
ord => ord.ProductID,
prod => prod.ProductID,
(ord, prod) => new
{
ord.ProductID,
OrderAmount = ord.Quantity * prod.Price
});
If you order does not refer back to a customer, the trick is to first create a dataset which keeps the customers and orders linked together:
customers
.SelectMany(c => c.Orders.Select(o => new {
cust = c,
ord = o
}))
Then on this CustomerOrder (co) you can apply your join:
...
.Join(products,
co => co.ord.ProductID,
prod => prod.ProductID,
(co,prod) => new {
co.cust.Name,
co.ord.ProductID,
OrderAmount = ord.Quantity * prod.Price});

LINQ data from view model using nested SQL [duplicate]

This question already has answers here:
LEFT JOIN in LINQ to entities?
(7 answers)
Closed 4 years ago.
I can access the data using the following TSQL:
select Sweets.*, Qty
from Sweets
left join (select SweetID, Qty from carts where CartID = '7125794e-38f4-4ec3-b016-cd8393346669' ) t
on Sweets.SweetID = t.SweetID
But I am not sure of how to achieve the same results on my web application. Does anyone know how this could be achievable using LINQ?
So far i have:
var viewModel = new SweetViewModel
{
Sweet = db.Sweets.Where(s => db.Carts.Any(c => c.SweetID == s.SweetID))
};
Edit: Sorry I should of specified that I am using a View model of the 2 classes:
View model:
public class SweetViewModel
{
public IEnumerable<Sweet> Sweet { get; set; }
public IEnumerable<Cart> Cart { get; set; }
//public Cart OrderQty { get; set; }
}
public class Sweet
{
public int SweetID { get; set; }
public int CategoryID { get; set; }
public Category Category { get; set; }
public string SweetName { get; set; }
public bool Singular { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public virtual ICollection<Cart> Carts { get; set; }
}
public class Cart
{
[Key]
public int RecordID { get; set; }
public string CartID { get; set; }
public int SweetID { get; set; }
public int PemixID { get; set; }
public int Qty { get; set; }
public System.DateTime DateCreated { get; set; }
public Sweet Sweet { get; set; }
public PreMix PreMix { get; set; }
}
The following will work
from sweet in db.Sweets
join cart in db.Carts
on sweet.SweetID equals cart.SweetID into swct
from sc in swct.DefaultIfEmpty()
select new { Sweet = sweet, Qty = sweet.Key == sc.Key ? sc.Qty : 0 }
var res=(from sweet in db.Sweets
join cart in db.Carts.Select(x=>new{x.SweetID,x.Qty})
on sweet.SweetID equals cart.SweetID
into r11
from r1 in r11.DefaultIfEmpty()
select new {sweet,r1})
.Select(x=>new
{
Sweet=x.sweet,
Qty=x.r1?.Qty
})
.ToList();
This will get you the equivalent result to your sql query.
res will be List<a> where a is anonymous class and it's structure will be
{Sweet,Qty}.
You should be using the LINQ join function.
For my example, I have also used an altered version of your SQL query which I believe to be identical:
SELECT sweets.*, carts.Qty
FROM sweets LEFT JOIN carts ON sweets.SweetID = carts.SweetID
WHERE carts.CartID = '7125794e-38f4-4ec3-b016-cd8393346669'
Then I translated this new query to LINQ with the JOIN function.
var cartId = '7125794e-38f4-4ec3-b016-cd8393346669'
var query = db.Sweets // table in the "from" statement
.GroupJoin(db.Carts, // all carts for that sweet will be joined into [sweets -> Cart[]]
cart => cart.SweetID, // the first part of the "on" clause in an sql "join" statement
sweet => sweet.SweetID, // the second part of the "on" clause)
(sweet, carts) => new { Sweet = sweet, Carts = cart }) // create new compound object
.SelectMany(
sweetsCarts => sweetsCart.Carts.DefaultIfEmpty(), //show the sweet even if there is no cart
(x,y) => new { Sweet = x.Sweet, Cart = y });
.Where(sweetsCart => sweetsCart.Cart.CartID == cartId); // restrict your cartID
Basically, the Join function makes a list of compound objects that contain a Sweet object and a Cart object with each list entry, hence why you can access sweetsCart.Cart.CartID or sweetsCart.Sweets.SweetID.
The name on the left side of the => can be anything you want by the way, it's just an identifier for LINQ. I chose to call it "sweetsCart".
The GroupJoin with the SelectMany makes it possible to do a Left Join.

One to Many LINQ Query - compiler error

Please see the LINQ query below:
var test = from s in db.Students
join c in db.Courses on s.courseid equals c.id into d
where s.name.StartsWith("Bert")
select new Student { id=s.id,name=s.name, Course = d.Select(x => x.name) };
A student links to one courses. Therefore the value of Course in the above should be a collection of courses. However, there is a compiler error (System.ArguementNullException). What am I doing wrong?
I am competent using SQL, however I am new to LINQ. Please see the SQL from the database below:
***Student Class***
public partial class Student
{
public int id { get; set; }
public string name { get; set; }
public Nullable<int> age { get; set; }
public Nullable<int> courseid { get; set; }
public virtual Course Course { get; set; }
}
Course Class
public partial class Course
{
public Course()
{
this.Students = new HashSet<Student>();
}
public int id { get; set; }
public string name { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
It seems you are just trying to get your query to also retrieve the Course navigation property for your students. As such, all you need to do is Include it:
var students = db.Students
.Include(s => s.Course)
.Where(s => s.Name.StartsWith("Bert");

LINQ JOIN two tables and viewing both columns

I Have two tables
tblPhases
And
tblTruck
I JOINED these two but I can't seem to use both columns on both tables.
Phase.cs
public class Phases
{
public int Id { get; set; }
public int TruckId { get; set; }
public virtual RTrucks Truck { get; set; }
public string Checkin { get; set; }
public string ChassisPrep { get; set; }
public string Floor { get; set; }
public string Body { get; set; }
public string Paint { get; set; }
public string Finished { get; set; }
}
RTruck.cs
public class RTrucks
{
public int Id { get; set; }
public string ChassisManufacturer { get; set; }
public string ChassisModel { get; set; }
}
Service.cs
[OperationContract]
List<Phases> GetPhasesbyTruck(int TruckId);
Service.svc.cs
public List<Phases> GetPhasesbyTruck(int TruckId)
{
TruckDb db = new TruckDb();
List<Phases> r = new List<Phases>();
var join = from p in db.RTrucks
join t in db.Phases
on p.Id
equals t.TruckId
where t.Id == TruckId
select p;
foreach (var item in join)
{
r.Add(new Phases()
{
//this is not the actual coding but just a demonstration of what is
//actually happening
// Phases item like Id = tblTruck item like truckId
});
}
return r;
}
I cannot call the columns from the tblPhases table only from the tbltrucks table, and also how can I use the Phases.cs and RTruck.cs together in my foreach so I can access both class items?
First of all, following code
var join = from p in db.RTrucks
join t in db.Phases
on p.Id equals t.TruckId
select p;
is enough to make a join between two tables. you do not need to add an extra where clause to it. Secondly, you can either select properties from both tables as mentioned by Arianit in his answer or you can just select Phases as in above query and then you can access Truck through navigation properties like
foreach (var p in join){
int truckId = p.Truck.Id;
string chesis = p.Truck.ChassisManufacturer;
string model = p.Truck.ChassisModel;
}
The benefit of second approach is that you don't need a new class to populate properties from both tables. you can access all information through navigation properties.
try this:
var join = from p in db.RTrucks
join t in db.Phases
on p.Id equals t.TruckId
select new {TruckName = p.TruckName, //depends from your table column name
PhasesName = t.PhaseName
};
foreach (var item in join)
{
Console.WriteLine(item.TruckName + "\t" + item.PhaseName);
}
Remove the where from your linq

Adding list values to a LINQ query

I have the following object (view model) that I want to use:
public class assignmentViewModel
{
public string choreName { get; set; }
public List<string> personName { get; set; }
}
LINQ statement:
var avm = (from a in Assignments
join c in Chores on a.ChoreID equals c.ChoreId
join p in Persons on a.PersonID equals p.PersonID
select new assignmentViewModel
{
personName = p.PersonName.ToList(),
choreName = c.ChoreName
}).ToList();
I can have multiple people in an assignment. I want to be able to pull back my data into this ViewModel. The error I'm getting currently is:
Cannot implicitly convert type 'System.Collections.Generic.List<char>'
to 'System.Collections.Generic.List<string>'
My data (if it helps) is:
chore #1
person 1
chore #2
person 1
person 2
The Person model is:
public partial class person
{
public int personID { get; set; }
public string personName { get; set; }
public System.DateTime personDOB { get; set; }
public string personEmail { get; set; }
public string personPhone { get; set; }
public string personPhoneCarrier { get; set; }
public bool isActive { get; set; }
}
You are looking for Grouping here, you need to group the records based on choreName, I would do it like this:
(from a in Assignments
join c in Chores on a.ChoreID equals c.ChoreId
join p in Persons on a.PersonID equals p.PersonID
select new
{
person = p,
choreName = c.ChoreName
})
.GroupBy(x => x.choreName)
.Select(g => new assignmentViewModel
{
personName = g.Select(x => x.person.PersonName).ToList()
choreName = g.Key
}).ToList();

Categories