I'm trying to edit subjects which isn't selected by student anymore. This is how my db looks like:
Using editStudent query I'm selecting items which is edited and using studentSubject I'm retrieving subjects from db.
var editStudent = (from st in _db.Students
where st.Id == student.Id
select new Students
{
Id = student.Id,
Name = student.Name,
Surname = student.Surname,
BirthDate = student.BirthDate,
Gender = student.Gender,
Subject = (from q in student.Subject
select q).ToList()
}).FirstOrDefault();
var studentSubjects = (from q in _db.Subjects
where q.StudentId == student.Id
select q.Name).ToList();
How can I delete subjects which isn't selected anymore? (They will no long be in Subject list from first query)
For example:
in db was 2 subject math and english and user changed these and now he
has only math. first query will return only math in list and second
will return both math and english
You can easily select not used Subjects with
var subjectsNotUsed = _db.Subjects.Where(subject => subject.StudentId == null);
Then (if you are using Entity Framework 6) you can use .RemoveRange() so:
_db.Subjects.RemoveRange(subjectsNotUsed);
_db.SaveChanges();
If you use lower version of EF, you need to delete one by one:
foreach (var subject in notUsedSubjects)
{
_db.Subjects.DeleteObject(subject);
}
_db.SaveChanges();
But I think you should change the database schema. Student shouldnt be related to Subject. You should use table Students just for information about students, and table Subjects just for subjects. Third table (basic M:N relation) you will represent that student has some subject, like that:
DB Diagram
This enables you store each subject and student just once in your database.
Related
I want to call a procedure and access the navigation properties afterwards
using (DbContext c = new DbContext())
{
// System.Data.Objects.ObjectResult<Product>
List<Product> products = c.myProcedure(id).Include("ProductOptions").ToList();
// here the .Include() isn't available
}
my current approach is loading each navigation property seperately
using (DbContext c = new DbContext())
{
List<Product> products = c.myProcedure(id).ToList();
foreach(Product p in products)
{
if(!o.ProductOptions.IsLoaded)
p.ProductOptions.Load();
}
}
is working fine but super slow because of the subselect for each item.
Question: Is there a kind of Include() function or something else to speed up my code?
While using entity framework people tend to use Include instead of Select. The result seems to be the same, however there is a slight difference.
The difference between Include and Select is that Include fetches the complete object, while Select fetches only the selected data. Another difference is that your DbContext object remembers the Included data.
Databases are extremely optimized in fetching data. One of the slower parts of the query is the transfer of the selected data from the database management system to your process. Hence it is wise to limit the amount of fetched data.
Suppose you have database with Schools and their Students: a straightforward one-to-many relations: Every School has zero or more Students, every Student studies at exactly one School, using a foreign key to the School he attends.
Let's query School [10] with its thousand Students:
var result = dbContext.Schools
.Include(school => school.Students)
.Where(school => school.Id == 10)
.FirstOrDefault();
The Complete Student is transferred. Every Student will have the same foreign key SchoolId value 10. So this same number is transferred a thousand times (1001 if you also count School.Id), while you already know the value. what a waste of processing power!
When querying data, always use Select to fetch the data. Only use Include if you plan to update the fetched data
var result = dbContext.Schools
.Where(school => school.Id == 10)
.Select(school => new
{
// Select only the School properties you plan to use
Id = school.Id,
Name = school.Name,
Address = school.Address,
Students = school.Students
.Where(student => ...) // only if you don't want all Students
.Select(student => new
{
// again: select only the properties you plan to use
Id = student.Id,
Name = student.Name,
...
// not needed, you already know the value!
// SchoolId = student.SchoolId,
})
.ToList(),
})
.FirstOrDefault();
I've use new to create an anonymous type. This is the most efficient. If you really need to create Schools and Students, use new School and new Student
.Select(school => new School
{
...
Students = schoolStudents.Select(student => new Student
{
...
})
.ToList(),
})
A last remark. include(school.Students) can be found in using System.Data.Entity
I am learning to work with LINQ, EF etc. I have 2 auto generated edmx classes course and student. I have written a code to access the course id and course name. With the data I also want to fetch the data of the students names who have enrolled for the courses respectively. Following is my code.
testEntities1 t = new testEntities1();
var u = (from g in t.courses
select new
{
g.C_Id,
g.C_Name,
dd = g.student.Select(r=>r.S_Name)
}).ToList();
Now how to use/get data from the property dd which i have selected which contains all the student names who have enrolled for the course. I am able to put data to variable u. I just don't know how to proceed and get data out of dd. Please help. Following is the line for getting data of the course id and course name. How can I get the data of the student names.
List<course> ui = u.Select(d => new course() { C_Name = d.C_Name, C_Id = d.C_Id }).ToList();
First of all there is no need to fetch the data first into an anonymous type and then add it to your custom type course, you can do that directly as mentioned below. Now, since you have not mentioned the Type of course, I am considering following two possibilites:-
Case 1: If you have student names as public List<string> Students_Enrolled { get; set; } in course i.e. you want to fetch the list of students enrolled then:-
List<course> u = (from g in t.courses
select new course
{
C_ID = g.C_Id,
C_Name = g.C_Name,
Students_Enrolled = g.student.Select(r => r.S_Name).ToList()
}).ToList();
Case 2: If you have student names as public string Students_Enrolled { get; set; } i.e. if you want to fetch comma separated student names then you can do this:-
List<course> u = (from g in t.courses
select new course
{
C_ID = g.C_Id,
C_Name = g.C_Name,
Students_Enrolled = String.Join(",",g.student.Select(r => r.S_Name))
}).ToList();
I am creating App using Entity Framework.I have loaded the Database model from Database.here In the Course table Department is the Navigation Property,DepartmentID is the foreign key.I have created a gridview and set its Datasource to Course Table,but I want to see the Deaptment Name instead of department ID.I can use the Navigation properties like Department.count but how can I use the navigation property to get data (Department name) from Department Table.
Any one who can help me with this
THIS IS MY CODE
var result = (from o in ctx.Courses.Include("Department")
where o.Title == TextBox2.Text
select o
).First();
//GridView4.DataSourceID="";
GridView3.DataSource=result;
GridView3.DataBind();
If I dont use the First Function then i can't access the Department Name attribute,If i use the First() It say that
Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource.
please tell me how i can solve it?
I think Products and Categories tables in Northwind are similar than your need. I would write a query like this:
var ctx = new NorthwindEntities();
var query = from prod in ctx.Products
where prod.ProductName.StartsWith("C")
select new { prod.ProductName, prod.UnitPrice, prod.Category.CategoryName };
var result = (from c in dbContext.Course
select c).First();
if(!result.Department.IsLoaded)
{
result.Department.Load(); //this will load the course.Department navigation property
}
//Assuming you have 1 - 1 relationship with course to department
string departmentName = result.Department.Name;
or if you have 1 - M relationship with the department then:
foreach(Department d in result.Department)
{
Console.WriteLine(d.Name);
}
EDIT:
Instead of trying to load Department do the following
if(!result.DepartmentReference.IsLoaded)
{
result.DepartmentReference.Load()
}
How to: Explicitly Load Related Objects
I have the following database:
Student
StudentID
Name
LastName
Grade
GradeId
Name
GradeInstance
GradeInstanceId
GradeId
Name
Year
StudentInstance
StudentInstanceId
GradeInstanceId
StudentInstanceId
How can I retrieve a list of students from a given grade instance?
I'm using Entity Framework as my ORM so what would the LINQ query be like?
Edit: I need to return Student objects, not StudentInstance object because those don't contain the attributes I need to fill the GUI with.
So something like this doesn't work for me, unless I'm missing something here.
dbContext.StudentInstances.Where(s => s.GradeInstanceId == 1);
from s in dbContext.Student
join si in dbContext.StudentInst on s.StudentID equals si.StudentInstanceID
join g in dbContext.Grade on si.GradeInstanceID equals g.GradeID
where g.Name = ...
select s;
This should help you get started....
public static List<Student> GetStudents(int gradeId)
{
using (var context = new Entities())
{
List<Student> myList = (from s in dbContext.Student
join si in dbContext.StudentInst on s.StudentID equals si.StudentInstanceID
join g in dbContext.Grade on si.GradeInstanceID equals g.GradeID
where g.GradeId = gradeId
select s).ToList();
return myList;
}
}
Using slightly modified query :D
you could add a navigation property to your GradeInstance Entity to the collection of Student entities related to it (you actually have the possibility to do that from the Add Association wizard), then you could acess it simply with: gradeInstance.Students
Hope this helps
I have this classic scenario where I have a User table and a Contact table containing only UserId and ContactId columns (so it is a self many to many relationshsip). What I would like is a query that gives me a list of userIds with number of common contacts with the specified User. In plain old SQL I have the following query (contacts of user and user itself is filtered out to get facebook like friend suggestions):
SELECT COUNT(c1.ContactId) as CommonContact, c2.UserId
from Contacts as c1
inner join Contacts as c2 on c1.ContactId = c2.ContactId
Where c1.UserId = #Id AND c2.UserId != #Id
AND c2.UserId NOT IN (SELECT ContactId from Contacts Where UserId = #Id)
Group By c2.UserId
ORDER BY CommonContact Desc
This simple query works great but I can not figure out how to write the same query in LINQ to Entity, because in the Entity Framework model I have User entity that entity have Contact navigation property but the connection table is not there directly....
Thanks a lot for any help...
Didn't have time and try to run it but something like this should work.
public class Test
{
//simulate an IQueryable
private readonly IQueryable<Person> _people = new List<Person>().AsQueryable();
public void FindContactMatchCount(Guid personId)
{
//we'll need the list of id's of the users contacts for comparison, we don't need to resolve this yet though so
//we'll leave it as an IQueryable and not turn it into a collection
IQueryable<Guid> idsOfContacts = _people.Where(x => x.Id == personId).SelectMany(x => x.Contacts.Select(v => v.Id));
//find all the people who have a contact id that matches the selected users list of contact id's
//then project the results, this anonymous projection has two properties, the person and the contact count
var usersWithMatches = _people
.Where(x => idsOfContacts.Contains(x.Id))
.Select(z => new
{
Person = z, //this is the person record from the database, we'll need to extract display information
SharedContactCount = z.Contacts.Count(v => idsOfContacts.Contains(v.Id)) //
}).OrderBy(z => z.SharedContactCount)
.ToList();
}
}