One to Many LINQ Query - compiler error - c#

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");

Related

Data is ignored at ThenInclude() for nested List property

I have more of a special case, and I am not sure what the reason is for what my issue is happening.
I have a class A with nested class B which also has a nested class C
public class A
{
[Key]
public int Id { get; set; }
public int Name { get; set; }
public List<B> Bs{ get; set; }
}
public class B
{
[Key]
public int Id { get; set; }
public int Name { get; set; }
public List<C> Cs{ get; set; }
[ForeignKey(nameof(A))]
public int AId{ get; set; }
}
public class C
{
[Key]
public int Id { get; set; }
public int Name { get; set; }
[ForeignKey(nameof(B))]
public int BId{ get; set; }
}
Now the problem that I have is that the values of class C aren't included into class B when class A is called alongside with Include(), respectively ThenInclude() methods.
The way values are brought from DB Tables is using a normal SQL Query combined with LINQ.
var query = from queryResult in _dbSet.Include(a => a.B).ThenInclude(B => b.C)
select new ResultModel()
{
Name = queryResult.Name
B = queryResult.B
}
The normal properties of B are being brought from db and mapped automatically to it, but the values for the nested List property aren't for some reason.
Do I need to make additional changes to select query in order for values of nested class C property be mapped into class B?
I hope the question was not too confusing.
The problem I had was the property being ignored because of not being included in the select. So the solution would be to change from
var query = from queryResult in _dbSet.Include(a => a.B).ThenInclude(B => b.C)
select new ResultModel()
{
Name = queryResult.Name
B = queryResult.B
}
to
var query = from queryResult in _dbSet.Include(a => a.B).ThenInclude(B => b.C)
select new ResultModel()
{
Name = queryResult.Name
B = queryResult.B.Select(x =>
new C {
Id = x.Id,
Name = x.Name,
}).ToList()
You can read more about this issue here.

How do I apply a Where clause to an entity that's a couple layers deep in a LINQ expression?

I'm working on a LINQ expression that will pull in related tables to a Person table we've got. The query I've written does work, but it takes a long time to run and brings back more data than I need. Here's the LINQ expression I have now:
using (var ctx = new AppEntities())
{
People = ctx.People.Where(p => p.Inactive == false)
.Include(p => p.Agency)
.Include(p => p.PersonnelCertifications.Select(pc => pc.CertificationType))
.OrderBy(p => p.LastName)
.ThenBy(p => p.FirstName)
.ToList();
}
We're working with .NET 4.5.2 and EF 6.4. The Person table has a relatonship with the PersonnelCertification table. And that has a relationship with the CertificationType table. Ideally, what I need to add is a filter so that only CertificationType.CertType == "Operator". I tried adding a Where clause after the Include of PersonnelCertifications, but that didn't work. The second Where clause was still only working with the Person table. Is there a way of doing what I want? If so, how is this done?
Here's the table definitions with extraneous fields removed for brevity:
public partial class Person
{
public Person()
{
PersonnelCertifications = new HashSet<PersonnelCertification>();
}
public long ID { get; set; }
public virtual ICollection<PersonnelCertification> PersonnelCertifications { get; set; }
}
public partial class PersonnelCertification
{
public long ID { get; set; }
public long CertificationTypeID { get; set; }
public long PersonID { get; set; }
public virtual CertificationType CertificationType { get; set; }
public virtual Person Person { get; set; }
}
public partial class CertificationType
{
public CertificationType()
{
PersonnelCertifications = new HashSet<PersonnelCertification>();
}
public long ID { get; set; }
[Required]
[StringLength(30)]
public string CertType { get; set; }
public virtual ICollection<PersonnelCertification> PersonnelCertifications { get; set; }
}
.Where(p => p.PersonnelCertifications.Any(pc => pc.CertificationType == "Operator")) should give you the people you are looking for.

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 Eager load

I am working with the following technologies: C#, SQL Server, ASP.NET and Entity Framework and Linq.
I have a many to many relation , using eager load. I want to get all the courses where a student is inscribed. As you can see I have a one to many relation from student to inscribe table.
The model classes:
public class Courses
{
[Required]
public int Id { get; set; }
//more properties here
public student stud { get; set; }
}
public class Enroll
{
[Key]
public intId { get; set; }
//properties here
[Required]
public string StudentId{ get; set; }
public Courses Courses{ get; set; }
}
public class student{
public intId { get; set; }
//other properties
public Inscripe Inscription {get;set}
}
This is what my controller:
public IEnumerable<course> GetCoursesStudent(Int studentId)
{
//some code here to validate
var result = _dbContext
.Enroll.Include(c => c.Courses)
.Where(c => c.StudentId == studentId)
.SelectMany(c => c.Courses).ToList();
}
problem :
I receive an error from the SelectMany: the type aregument for method Queribly.selectMany(IQueryableExpression>> can not be infered from the usage.
How can I fix it?
The type you specified for the IEnumerable is wrong. It should be "Courses" instead of "course":
public IEnumerable<Courses> GetCoursesStudent(Int studentId)
{
var result = _dbContext
.Enroll
.Include(c => c.Courses)
.Where(c => c.StudentId == studentId)
.SelectMany(c => c.Courses)
.ToList();
}
And the "Courses" property of Enroll class should be an enumerable:
public class Enroll
{
[Key]
public intId { get; set; }
[Required]
public string StudentId { get; set; }
public IEnumerable<Courses> Courses { get; set; }
}

Error iterating the query results

I have a context called companyContext. There are Three tables Reports,Logs and Employees.I am given a case id and I need to get a list of all the employees who belong to a certain case (and a log if there is one, but i dont need to worry about that yet). So I made a query get all the employees where EmployeeID is equal to Employee.ID, where the Reports CaseID is eaual to case.id. However, its not reutning a list, its returning a Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable}]
Am I making the query correctly ? Thanks guys.
var employees = await context.Reports.Include(s => s.Employee)
.ThenInclude(e => e.ID)
.AsNoTracking()
.Where(r => r.CaseID == Case.Id);
Models
public class Log
{
public int ID { get; set; }
public string Input { get; set; }
public string Tag { get; set; }
public DateTime LogDate { get; set; }
public ICollection<Report> Reports { get; set; }
}
public class Employee
{
public int ID { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public ICollection<Report> Reports { get; set; }
}
public class Report
{
public int ID { get; set; }
public string CaseID { get; set; }
public int EmployeeD { get; set; }
public int? LogID { get; set; }
public Employee Employee { get; set; }
public Log Log { get; set; }
}
SingleOrDefaultAsync
throws an exception if there is more than one element in the sequence.
So you should not be using SingleOrDefaultAsync if you expect multiple records.
If I'm interpreting your question properly and you want employees themselves, you could start with them and then narrow via the Employee class's .Reports navigation property.
var employees = await context.Employees.AsNoTracking()
.Where(e => e.Reports.Any(r => r.CaseID == case.Id))
.ToListAsync();
So entity is actually like using a real database. I had to do a where and select clause, but also a toList to convert it into something readable.
var employees = await context.Reports.Where(r => r.CaseID == Case.Id);
.Select(r => r.Employee)
.ToList();

Categories