Adding list values to a LINQ query - c#

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

Related

LINQ - nested GroupJoin

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

Cannot implicitly convert type 'System.Collections.Generic.List<AnonymousType#1>' to 'System.Collections.Generic.List<Model.Common.Province>'l C# MVc

My Code is here below
List<Province> ldb = (from k in db.Provinces
join c in db.Countrys on k.CountryID equals c.CountryID
select new { k.ProvinceID, k.ProvinceName, c.CountryName }).ToList();
ViewBag.Province = ldb;
I want display simple in table and join with country to display country name in table. The error is following
Error 6 Cannot implicitly convert type
'System.Collections.Generic.List<AnonymousType#1>' to
'System.Collections.Generic.List<Model.Common.Province>'
public class Province
{
[Key]
public int ProvinceID { get; set; }
[Required]
public int CountryID { get; set; }
[Required]
public string ProvinceName { get; set; }
public string Description { get; set; }
[NotMapped]
public List<Country> CountryCollection { get; set; }
}
its clear from the error that you cannot convert anonymous type to generic type and its not proper way to use domain models into view so you should create another class which is called ViewModel this class is used for specific view in your case you need both country name and province so your ViewModel will be
public class ProvinceCountryViewModel
{
public int ProvinceID { get; set; }
public string ProvinceName { get; set; }
public string CountryName { get; set; }
}
and your linq will be
List<ProvinceCountryViewModel> ldb = (from k in db.Provinces
join c in db.Countrys on k.CountryID equals c.CountryID
select new ProvinceCountryViewModel{ProvinceID = k.ProvinceID, ProvinceName=k.ProvinceName,CountryName = c.CountryName }).ToList();
ViewBag.Province = ldb;
and in view
#foreach (var value in (List<ProvinceCountryViewModel>) ViewBag.Province )
{
// here you can access properties like value.CountryName
}
When using select in LINQ, you need to create new Province class instance, not anonymous object.
select new Province{ /*assign properties here*/ }).ToList();
Return the province type as mentioned below
List<Province> ldb = (from k in db.Provinces
join c in db.Countrys on k.CountryID equals c.CountryID
select new Province { k.ProvinceID, k.ProvinceName, c.CountryName }).ToList();
ViewBag.Province = ldb;

Convert the result of LINQ query to list of Custom Model

Good evening everyone.
I have a LINQ to SQL query result, and I want to convert this result to a list of Custom Model class
down here the Class Model:
public class Beneficiary
{
public int BeneficiaryId { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public DateTime? CreatedAt { get; set; }
[Column(TypeName = "date")]
public DateTime? BithDate { get; set; }
public int? CommunityId { get; set; }
public virtual Community Community { get; set; }
[Required]
public string Gender { get; set; }
public string IdNumber { get; set; }
[Required]
[StringLength(50)]
public string AppName { get; set; }
[StringLength(50)]
public string BeneficiaryNumber { get; set; }
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<BeneficiaryProject> BeneficiaryProjects { get; set; }
public virtual ICollection<Card> Cards { get; set; }
public virtual ICollection<Invoice> Invoices { get; set; }
}
and here is the query that I used :
var list = (from B in db.Beneficiaries
join ben in db.BeneficiaryProjects on B.BeneficiaryId equals ben.BeneficiaryId
where (ben.CardNeeded == true && ben.ProjectId == temp.ProjectId)
select new Beneficiary()
{
BeneficiaryId = B.BeneficiaryId,
FirstName = B.FirstName,
LastName = B.LastName,
IdNumber = B.IdNumber,
Gender = B.Gender
});
the above query is successfully executed, but when I convert the var list to a list of Beneficiary as following:
List<Beneficiary> list1 = list.ToList<Beneficiary>();
but I got the below exception :
An exception of type 'System.NotSupportedException' occurred in EntityFramework.SqlServer.dll but was not handled in user code
Additional information: The entity or complex type 'E_Voucher.Models.Beneficiary' cannot be constructed in a LINQ to Entities query.
Make conversion to custom class after retrieving the results from EF:
var list = (
from B in db.Beneficiaries
join ben in db.BeneficiaryProjects on B.BeneficiaryId equals ben.BeneficiaryId
where (ben.CardNeeded == true && ben.ProjectId == temp.ProjectId)
select new {
B.BeneficiaryId,
B.FirstName,
B.LastName,
B.IdNumber,
B.Gender
}
).AsEnumerable()
.Select(B => new Beneficiary() {
BeneficiaryId = B.BeneficiaryId,
FirstName = B.FirstName,
LastName = B.LastName,
IdNumber = B.IdNumber,
Gender = B.Gender
}).ToList();
Note: The reason you get the error on running ToList is that EF queries defer execution. Your query may be incorrect, but the only way to find out is to try getting its results.
The reason why you are getting this error
(in this line:)
List<Beneficiary> list1 = list.ToList<Beneficiary>();
is that you are trying to convert a Linq Query variable to a List.
All you really need to do is the following:
List<Beneficiary> list1 = list.ToList();
the query you have for temp.ProjectID:
var projects = db.Projects.Where(p => p.ContractId == id); foreach (var temp in projects)
should be something like:
int ProjectId = (from P in db.Projects where P.ContractId == id select P.ProjectId).FirstOrDefault();
and then take that value and put into your next query:
... where (ben.CardNeeded == true && ben.ProjectId == ProjectId)
I believe you can't project your object of type Beneficiary from within LINQ to Entities to a mapped entity object.
Try this:
db.Beneficiaries.Join(db.BeneficiaryProjects, B => B.BeneficiaryId, ben => ben.BeneficiaryId, (B, ben) => new { B, ben })
.Where(x => x.ben.CardNeeded == true && x.ben.ProjectId == temp.ProjectId)
.Select(x => x.B)
.ToList();
It's a lamda expression that should do the same thing as your LINQ query but will select your Beneficiary model object and map it. The ToList() can be replaced with an AsQueryable() or AsEnumerable() based on your need.

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

How to perform a join of two sets and populate navigation properties

Let's say I have two classes, Teapot and Cup:
public class Teapot
{
[Key]
[Column("Id", TypeName = "int")]
public int Id { get; set; }
public int MaterialId { get; set; }
public int ColorId { get; set; }
[ForeignKey("MaterialId")]
public virtual Material Material { get; set; }
[ForeignKey("ColorId")]
public virtual Color Color { get; set; }
}
and
public class Cup
{
[Key]
[Column("Id", TypeName = "int")]
public int Id { get; set; }
public int MaterialId { get; set; }
public int ColorId { get; set; }
[ForeignKey("MaterialId")]
public virtual Material Material { get; set; }
[ForeignKey("ColorId")]
public virtual Color Color { get; set; }
}
This is my ViewModel:
namespace ViewModel.Data
{
public class TeapotsWithInfo
{
public Model.Data.Teapot Teapot { get; set; }
public Model.Data.Cup Cup { get; set; }
}
}
For my ViewModel, I need to perform a join on MaterialId and ColorId and to have some navigation propeties like Teapot.Material.Manufacturer included. So, I've tried following queries:
This throws "LINQ to Entities only supports casting EDM primitive or enumeration types"
(from t in db.Teapots
join c in db.Cups
on new { t.MaterialId, t.ColorId } equals new { c.MaterialId, c.ColorId }
where t.Id == id
select new ViewModel.Data.TeapotsWithInfo { Teapot = t, Cup = c })
.Include(t => t.Material.Manufacturer).SingleOrDefault();
This seems to ignore the Include
(from t in db.Teapots.Include(t => t.Material.Manufacturer)
join c in db.Cups
on new { t.MaterialId, t.ColorId } equals new { c.MaterialId, c.ColorId }
where t.Id == id
select new ViewModel.Data.TeapotsWithInfo { Teapot = t, Cup = c }).SingleOrDefault();
Now I found some answers here that suggest to enumerate and then to perform another select, but I'd rather catch the data in one go.
You're having difficulties including navigation properties because queries with joins or projections ignore eager loading (see this).
Unfortunately, you will need to do what you seem to be avoiding: pull the data from the database first, then do an additional select to build your ViewModel (which will have the relationships loaded from the original query). The additional select should be a rather trivial operation, though, since you're not performing an additional load from the database, and the enumerable should only contain a single item.
(from t in db.Teapots.Include(t => t.Material.Manufacturer)
join c in db.Cups
on new { t.MaterialId, t.ColorId } equals new { c.MaterialId, c.ColorId }
where t.Id == id
select new
{
Teapot = t,
Cup = c,
Material = t.Material,
Manufacturer = t.Material.Manufacturer,
})
.AsEnumerable()
.Select(a => new ViewModel.Data.TeapotsWithInfo
{
Teapot = a.Teapot,
Cup = a.Cup
})
.SingleOrDefault();
Source
http://thedatafarm.com/data-access/use-projections-and-a-repository-to-fake-a-filtered-eager-load/

Categories