sub query within linq select statement - linq

I want to get a list of student details and their attended hours using LINQ query in Entity Framework.
I am using the following query to bind a list of students details and the total hours attended by them .
var students = (from pa in db.tblstudents
select new studentModel
{
Name = pa.Name,
StudentID = pa.ID,
RollNO = pa.rollNO,
Department = pa.Department,
Phone = pa.Phone,
Address = pa.Address,
TotalHours = (from time in tblhours
where time.studentID = pa.ID
select time.hours).Sum()
}).Distinct().AsQueryable().ToList();
my studentModel looks like below:-
public class StudentModel
{ public string Name { get; set; }
public int StudentID { get; set; }
public string RollNO{ get; set; }
public string DepartMent{ get; set; }
public long Phone{ get; set; }
public string Address{ get; set; }
public string Address2{ get; set; }
public decimal TotalHours{ get; set; }
}
tblhours and tblstudent are related by studentid and if i want to get hours attended by all students within the particular timeperiod how can i do that?
I get the error as "Only parameterless constructors and initializers are supported in LINQ to Entities" when i try the above query
I think I am going wrong when I was selecting total hours using a subquery...

Make sure you have an empty constructor in the StudentModel class
BTW, if you set up the relationship properly you should be able to do this as well
TotalHours = pa.tblhours.Sum(time=>time.hours)

Related

EF Core non-existant column in query, how to rectify?

working my way through an EF core project I have inherited and am quite new when it comes to LINQ/EF core.
I'll cut this back to simplify things, and will demonstrate my problem with 4 basic tables.
Customer
CustomerId
Name
Contact
1
John
john#gmail.com
2
Peter
peter#gmail.com
CustomerTrade
Id
CustomerId
OtherDetail
T1
1
xyz
T2
1
abc
CustomerTradeParameter
ParamID
TradeId
Value
X
1
1234
Y
1
5678
CustomerTradeParameterType
ParamID
Name
OtherGenericInfo
X
Hello
Null
Y
Test
Null
Models
public class Customer : AuditableEntity
{
public long CustomerId { get; private set; }
public string Name { get; private set; }
public string Contact { get; private set; }
public virtual ICollection<CustomerTrade> CustomerTradeList{ get; set; }
protected Customer()
{ }
public class CustomerTrade : AuditableEntity
{
public long Id { get; private set; }
public long CustomerId { get; private set; }
public string OtherDetail { get; private set; }
public virtual ICollection<CustomerTradeParameter> CustomerTradeParameterList { get; set; } = new List<CustomerTradeParameter>();
protected CustomerTrade() // For EF Core
{ }
public class CustomerTradeParameter : AuditableEntity
{
public long TradeId { get; set; }
public string ParameterType { get; private set; }
public string Value{ get; private set; }
protected CustomerTradeParameter() // For EF Core
{ }
}
DTOs
public class CustomerTradeDto : AuditableDto
{
public long Id { get; set; }
public long CustomerId { get; set; }
public virtual ICollection<CustomerTradeParameterDto> CustomerTradeParameterList { get; set; } = new List<CustomerTradeParameterDto>();
}
public class CustomerTradeParameterDto : AuditableDto
{
public long TradeId { get; set; }
public string ParameterType { get; set; }
public string Value { get; set; }
}
The below query is attempting to retrieve a specific trade, and a list of its relevant parameters.
IQueryable<CustomerTradeDto> foundTrade = _database.CustomerTrade
.Select(trade => new CustomerTradeDto
{
Id = trade.Id,
CustomerId = trade.CustomerId,
CustomerTradeParameterList = trade.CustomerTradeParameterList.Select(param => new CustomerTradeParameterDto{
ParameterType = param.ParameterType,
TradeId = customerTrade.Id,
Value = param.Value
// (second question written further below) - If I wanted to additionally retrieve the CustomerTradeParameterType record
// (to get the name of this parameter), how would I embed this join?
// I originally had ParameterType defined as a "CustomerTradeParameterType" instead of a string,
// but am not sure how to add this extra join inside this select?
}).ToList()
})
.Where(e => e.Id == find.Id);
The .Select on CustomerTradeParameterList is raising the following error:
Microsoft.Data.SqlClient.SqlException (0x80131904): Invalid column name 'CustomerTradeId'.
I understand EF core is attempting to define a column that it is unsure of, but I am not sure how to fix this? The correct column is TradeId.
Secondly, I have an additional question regarding joins inside a sub .Select. In the query above I have a comment outlining the question.
Appreciate any tips you can provide!

MVC viewmodel and a linq query

I finally (after 4 weeks) got a result for a MVC5 project and it went well.
Now I'm trying to "limit" the number of results and it's flagging an error:
'Stoopid' is a type, which is not valid in the given context
'Student' is a type, which is not valid in the given context
Here's the model:
namespace viewModelA
{
public class Teacher
{
public int TeacherId { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
public class Student
{
public int StudentId { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string EnrollmentNo { get; set; }
}
public class Stoopid
{
[Key]
public int StoopID { get; set; }
public DateTime stopDt { get; set; }
}
public class ViewModel
{
public IEnumerable<Teacher> Teachers { get; set; }
public IEnumerable<Student> Students { get; set; }
public IEnumerable<Stoopid> Stoopids { get; set; }
}
}
and this is the linq query - notice that Teacher is fine, but Student and Stoopid are NOT. And they are all in the same .cs file. Am I missing something?
var result = (from t in Teacher
join s in Student on t.TeacherId equals s.StudentId
join st in Stoopid on s.StudentId equals st.StoopID
where t.TeacherId == 2
select new
{
TeacherID= t.TeacherId,
Code = t.Code,
t.Name,
s.StudentId,
sCode =s.Code,
sName=s.Name,
stopDt= st.stopDt
})
Edit: I added the relevant code to the HomeController. I also ran this thru LINQPad5 and it works fine so I don't know what's the deal
HomeController
You're referencing the class names in your Linq query, which is why it is throwing that error. You need to reference the actual List objects instead.
var result = (from t in mymodel.Teachers
join s in mymodel.Students on t.TeacherId equals s.StudentId
join st in mymodel.Stoopids on s.StudentId equals st.StoopID
where t.TeacherId == 2
select new
{
TeacherID= t.TeacherId,
Code = t.Code,
t.Name,
s.StudentId,
sCode =s.Code,
sName=s.Name,
stopDt= st.stopDt
})

Unable to create a constant value of type ''YYY". Only primitive types or enumeration types are supported in this context

I have an entity Person and a view model that contains a collection of persons.
public class ViewModel
{
public string Name { get; set; }
public string Description { get; set; }
public int Project { get; set; }
public ICollection<PersonsViewModel> PersonCollection { get; set; }
}
public class PersonsViewModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
These are my tries:
1e:
ICollection<Person> prsn = new PersonRepository().GetAll().Where(x => vm.PersonCollection.Select(y => y.Id).Contains(x.Id)).ToList();
2e:
ICollection<Person> prsn = (from st in new PersonRepository().GetAll()
from qw in cm.PersonCollection
where st.Id == qw.Id
select st).ToList();
Based on this blog post, 3e:
ICollection<Person> prsn = (from st in new PersonRepository().GetAll()
from qw in cm.PersonCollection
where st.Id.Equals(qw.Id)
select st).ToList();
What i'm trying to do is, select the person entity from the datacontext, based on the person id's from the view model. In all the 3 (i did more tries but i lost count) cases i ended up with the run time error as described in the title.
I also found the same question asked here on SO, but it was kind of hard to compare it with mine as there was no extra code available like model/entity.
Can someone point me into the right direction?
You can do like this ,
var Ids=vm.PersonCollection.Select(y => y.Id).ToArray();
ICollection<Person> prsn =
new PersonRepository().GetAll().Where(x => Ids.Contains(x.Id)).ToList();

Entity framework is making the id null on insert

I'm getting the following error when I try to insert a new row in one of my relational tables. I have the following two models:
public class CompanyCredit
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int creditId { get; set; }
public int planCredit { get; set; }
public DateTime? PlanCreditExpirationDate { get; set; }
}
And
public class CompanyInformation
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int id { get; set; }
[Required]
[DisplayName("Company Name:")]
public string companyName { get; set; }
public string timeZone { get; set; }
//navigation Properties
public virtual CompanyCredit Credits { get; set; }
}
And this Relation in the dbContext
modelBuilder.Entity<CompanyInformation>().HasOptional(e => e.Credits);
I'm trying to add a record inside CompanyCredit table like so:
if (_company.Credits == null)
{
var _credits = new CompanyCredit();
_credits.planCredit = 200;
_credits.PlanCreditExpirationDate = System.DateTime.UtcNow.AddMonths(1);
_company.Credits = _credits;
repo.InsertOrUpdate(_company, User.Identity.Name);
}
And Finally Insert or update just marks Company as changed and _credit as added like so:
_db.Entry(_credits).State = System.Data.EntityState.Added;
_db.Entry(Company).State = System.Data.EntityState.Modified;
_db.SaveChanges();
When this runs I get the following Error that I just can't seem to find the reason to.
Cannot insert the value NULL into column 'creditId', table 'Project.dbo.CompanyCredits'; column does not allow nulls. INSERT fails.
The statement has been terminated.
Thank in advanced for your help.
I found the problem was in the attribute [DatabaseGenerated(DatabaseGeneratedOption.Identity)] this should have been [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
I thought I would post this so others might benefit from it.
Could you please try reversing the order of entity state modification, just before the saveChanges call
_db.Entry(Company).State = System.Data.EntityState.Modified;
_db.Entry(_credits).State = System.Data.EntityState.Added;
_db.SaveChanges();

Is there a way to search two different collection and fill another collection using LINQ?

I have two different collections like below
public class Student
{
public int StudentID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Skills
{
public int SkillID { get; set; }
public int StudentID { get; set; }
public string Keyskill_Name { get; set; }
public int LastUsedYear { get; set; }
}
Here one student can contain multiple keyskils
I just want to fill a new collection like below
public class StudentDetails
{
public int StudentID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public list<string> Keyskill_Name { get; set; }
}
Please help me. Thanks in advance.
The GroupJoin LINQ method is the perfect solution for this case:
List<Student> students = new List<Student>();
List<Skills> skills = new List<Skills>();
List<StudentDetails> studentDetails = students.GroupJoin(skills, student => student.StudentID, skill => skill.StudentID, (student, skillsForStudent) => new StudentDetails
{
FirstName = student.FirstName,
LastName = student.LastName,
StudentID = student.StudentID,
Keyskill_Name = (from skill in skillsForStudent
select skill.Keyskill_Name).ToList()
}).ToList();
How to use the GroupJoin method?
Call the method from the set that gives a 1 to 1 relation with the result (Here for each Student, there is one and only one corresponding StudentDetails.
The first argument of the method is the set you want to "distribute" on the other items (Here the skills are "distributed" over the entire set of students)
The second and third arguments are used to explain how to make the collision between the elements of the first set and the elements of the second set. Lambda expressions are extremely useful in this case.
Lastly, the fourth argument, is used to define the result. It is a function with 2 parameters : an element of the first set (here students) and its corresponding elements in the second set that have been found using the collision.

Resources