How to update many to many relationship in Entity Framework - asp.net-mvc-4

enter image description here
I have a many-to-many relationship between students and courses as shown.
I want to update the courses for a given student (ex student Id = 10 has selected two courses with Id 2 and 6). I want to delete course Id 6 for student id 10 and add new course, say Id = 3.
I am using a code-first approach with Entity Framework 6 in ASP.NET MVC 4.
Can anyone help to solve this issue?
enter image description here

Your specifications weren't really clear about what you meant with the term "delete a Course": do you want to remove the Course from the database, so the Course does not exist anymore? Or do you want your Student to stop following this Course?
I'll describe both, therefore I have a student with 3 Courses:
Id = 2: will not be changed; Student already followed this Course and will keep following this Course
Id = 6: student will stop following this Course; Other Students might still be following this Course
Id = 7: Course will be removed from the database
Furthermore you have:
Id = 3: Student will start following this Course
many-to-many in entity framework
If you've configured your many-to-many relation according to the Entity Framework Code First many-to-many conventions You will have something similar as the following:
class Student
{
public int Id {get; set;}
// every Student attends zero or more Courses (many-to-many)
public virtual ICollection<Course> Courses {get; set;}
... // other properties
}
class Course
{
public int Id {get; set;}
// every Course is attended by zero or more Students
public virtual ICollection<Student> Students {get; set;}
... // other properties
}
public SchoolContext : DbContext
{
public DbSet<Student> Students {get; set;}
public DbSet<Course> Courses {get; set;}
}
This is all that entity framework needs to know that you wanted to configure a many-to-many relationship between Students and Courses. Entity Framework will create the junction table with the foreign keys to the Students and Courses for you. Whenever you access the ICollections entity framework will know that a join with the junction table is needed.
This can be done without the use of Attributes or Fluent Api. These are only needed if you want to deviate from the conventions, for instance if you want different table names or column names.
Back to your problem
We have the following Ids:
int idStudent = 10;
int idCourseToStartFollowing = 3;
int idCourseToStopFollowing = 6;
int idCourseToRemoveFromDb = 7;
using (var dbContext = new SchoolContext())
{
// Fetch the Student to update (has id 10)
Student studentToUpdate = dbContext.Students.Find(idStudent);
// this student starts following Course with Id 3
Course courseToStartFollowing = dbContext.Courses.Find(idCourseToStartFollowing);
studentToUpdate.Courses.Add(courseToStartFollowing);
// this student stops following Course with Id 6
Course courseToStopFollowing = dbContext.Courses.Find(idCourseToStopFollowing);
StudentToUpdate.Courses.Remove(courseToStopFollowing);
// Remove Course with Id 7 from the database
Course courseToRemove = dbContext.Find(idCourseToRemove);
dbContext.Remove(courseToRemove);
dbContext.SaveChanges();
}
Simple comme bonjour!

Related

Course has many students and many students belong to one course. How to find to which course a particular student belong?

there is a one to many unidirectional mapping from course to students
#Entity
Course {
#OnetoMany
List<Student> students;
}
#Entity
Student {
name;
age;
}
the database schema looks like
Course
id name duration fee
Student
rollno name age course_id
I am using Jpa repository for getting.
Can anyone please tell how to tell in which course a particular student belong to? Please note that i am not using bi-directioal mapping so student entity class has no reference to course.
Your mappings and DB schema are fine, and there is nothing overly special about your mapping situation - it is just a unidirectional mapping, and there is no need for force mapping it bidirectionally and/or fetching the student data if you don't need it.
JPQL: "Select c from Course c join c.students s where s.name = :name"
With spring boot
#Query("Select c from Course c join c.students s where s.name = ?1")
Course findCourseByStudentName(String name);
Your DB setup would need to be looked at to determine if the Course is unique - I'd guess it isn't, in which case you'll want to return a list of courses that might have this student name registered.
Your problem is in the database deign. No performant code will rescue you from that fate.
Typically there is a join table between Students and Courses that minimally maintains two keys - the studentId and the courseId.
Need to know the courses for a student? Select from the join table where studentId = ‘X’. Need to know students in a course? Select from the join table where courseId = ‘Y’.
I won't comment on the DB design. But solution for this problem is simple.
Basically you need to implement the relationship correctly.
#Entity
public class Course {
private Long id;
#OnetoMany(mappedBy = "course")
List<Student> students;
}
#Entity
public class Student {
private Long id;
private String name;
private Integer age;
#ManyToOne
#JoinColumn(name="course_id")
private Course course;
}
Then simply use the getter
Student student = studentRepo.findStudentById(123);
Course course = student.getCourse();
Here is the full reference: https://www.baeldung.com/hibernate-one-to-many

In Entity Framework 6, how to receive an entity as well as associated entities from a stored procedure?

Assume I have the following (the real case in more complex) CLR objects:
class Student {
public int Id {get;set;}
public int Age {get;set;}
public string Name {get;set;}
public ICollection<Grade> Grades {get;set;}
}
class Grade {
public int Id {get;set;}
public int Grade {get;set;}
public string Course {get;set;}
public virtual Student Student {get;set;}
public int StudentId {get;set;}
}
And the following stored procedure:
SELECT *
FROM Students AS S
INNER JOIN Grades AS G ON S.Id = G.StudentId
How do I execute the query from Entity Framework such that the resulting value will be a collection of Students with their Grades collections filled?
Obviously this could be achieved directly by using Entity Framework entities but the actual case is much more complex and the resulting EF query takes over 100 times more time than a stored procedure which achieves the same query. My only issue is how to receive the data when it returns
Just curious: is the stored proc imported in your EF-DbContext?
The result of this stored proc will be a complex result, in this case an object containing both the properties of the Student (Id, Age and Name) as well as the properties of the Grade (Id, Grade, Course, StudentId).
For each Grade, you will receive a separate record from the stored proc, which is mapped to the complex result i.e. object.
In order to get the list of Students with each having their own Grades-Collection, group the result object, e.g.
query = StoredProcResults.GroupBy(res => res.StudentId);

How to query Oracle database with NHibernate?

Help me translate this into proper NHibernate...
I have an Oracle database with 2 tables:
Employees:
Id (unique id)
FirstName (string)
LastName (string)
Location (string)
Locations:
Name (string)
Address (string)
As you can see the Employees table has a unique id, but the Locations table has no id whatsoever. The Name field is a regular field and is not unique.
In Oracle SQL I can run the following query:
SELECT *
FROM Employees e
LEFT OUTER JOIN Locations l
ON e.Location = l.Name
WHERE e.Id = 42
The location where the employee 42 is, has 2 rows in the Locations table, so this query returns 2 rows, one for each location found for employee 42.
Well, I can't figure out how to translate this into an NHibernate mapping + query (I use Fluent NHibernate). I tend to think that I should map Employees.Location as a Reference to Locations.Name, and so when running my HQL query it should return 2 objects, but NHibernate wouldn't let me retrieve a list from a Reference. So I tried HasMany but it doesn't work either, because NHibernate wants a field in Locations referring back to Employees, which kinda makes sense.
My HQL query looks like this:
select e
from Employees e
left join e.Locations l
where e.SGId like :sgId
Why can I do this in regular SQL and not in NHibernate?
Thanks.
I found the solution, you have to use HasMany(x => x.Locations) in conjunction with .PropertyRef("Location") so that hibernate knows that the field in Employee to be used for the join should be Location (and not the id of employee as is the default).
class Employee
{
public virtual int Id {get;set}
public virtual string FirstName {get;set;}
public virtual string LastName {get;set;}
public virtual string Location {get;set;}
public virtual IList<Location> Locations {get;set;}
}
class EmployeeMapping : ClassMap<Employee>
{
public EmployeeMapping()
{
Id(x=>x.Id);
Map(x=>x.FirstName);
Map(x=>x.LastName);
Map(x=>x.Location);
HasMany<Location>(x => x.Locations)
.PropertyRef("Location")
.KeyColumn("Name");
}
}
Also I found a few flaws in my approach due mainly to the weird legacy database I'm working with. That doesn't change the solution but I want to add that if one of the tables you're dealing with doesn't have unique ids, you're in trouble. Hibernate needs a unique id, so in my case I had to come up with a composite id made from multiple fields in order to make it unique. This is another debate but I wanted to mention it here to help whoever is researching this topic in the future.
For you mapping you need to use a collection/array/list/set object and a HasMany on your employees mapping for the location if it is going to return 2 locations for the one employee
class Employee
{
public virtual int Id {get;set}
public virtual string FirstName {get;set;}
public virtual string LastName {get;set;}
public virtual IList<Location> Location {get;set;}
}
class EmployeeMapping : ClassMap<Employee>
{
public EmployeeMapping()
{
Id(x=>x.Id);
Map(x=>x.FirstName);
Map(x=>x.LastName);
HasMany<Location>(x => x.Location)
.KeyColumn("Name")
.PropertyRef("Location")
.LazyLoad();
}
}

How to convert SQL aggregate query into NHibernate Criteria Query

I'm new to Criteria API in NHibernate. Can someone generate this piece of SQL using Criteria API in NHibernate?
select count(*)
from result where Student_id
in(
SELECT s.Student_id
from Department as d
JOIN Student s ON d.Dept_id=s.Dept_id
where d.Dept_id=2
)
and how to proceed through the Criteria API in NHibernate. P.S I don't want to use HQL so without HQL is it possible to generate this kind of sql in nhibernate?
You can use linq-2-nhibernate as well.
Given the following class structure:
public class Result{
public virtual Student Student {get; set;}
}
public class Student{
public virtual Department Department {get; set;}
public virtual int Id { get; set;}
}
public virtual Department {
public virtual int Id {get; set;}
public virtual IList<Student> Students {get; set;}
}
Here is your query using the Criteria API:
var studentidquery = DetachedCriteria.For<Student>()
.Add(Restrictions.Eq("Department.Id"),2)
.SetProjection(Projections.Property("Id"));
var count = session.CreateCriteria<Result>()
.Add(Subqueries.PropertyIn("StudentId", studentidquery))
.UniqueResult<int>();
Using the QueryOver API it would look like this:
var studentidquery = QueryOver.Of<Student>()
.Where(x=>x.Department.Id==2)
.Select(x=>x.Id);
var count = session.QueryOver<Result>()
.WithSubquery.WhereProperty(x => x.Id).In(studentidquery)
.Select(Projections.Count<Result>(r=>r.Id))
.UniqueResult<int>();
Also I don't think you need the join to Department in your SQL query as you already have DepartmentId as a foreign key in the Student table. No sense in joining to extra tables for no good reason.

Mapping a property to a field from another table in NHibernate

consider the following class:
class Order {
int OrderId {get; set;}
int CustomerId {get; set;}
string CustomerName {get; set;}
//other fields go here
}
which is mapped to Orders table. Is it possible to map the property CustomerName to the Customers table through the foreign key relation?
Yes, you can use the join mapping element for this. Another option is to map a view instead of a table. But if possible you should take the object-oriented approach and map the many-to-many relationship between Order and Customer.
I strongly suggest you don't use <join/> for this. Although it would accomplish what you requested, it creates other problems.
Instead, Order should have a relationship with Customer. You can then project the name if you want, although it's easier to just use order.Customer.Name.
So, it boils down to this:
1) Add Customer property to Order
public virtual Customer Customer { get; set; }
2) Map the property (in the example, CustomerId is the name of the FK column)
<many-to-one name="Customer" column="CustomerId"/>
3) If you specifically want to have a CustomerName property, project it from Customer:
public virtual string CustomerName { get { return Customer.Name; } }