MVC 3 many to many relationship and ViewModels - c#

I have a situation where I have a many to many relationship in my model between Student and Lesson. In most cases Lessons are 1 on 1 where a single student attends the lesson but there are situations where a lesson is shared by more than one student (hence the many to many).
So my ViewModel looks a little like this:
public class ScheduleViewModel
{
public Lesson Lesson { get; set; }
public List<StudentViewModel> Students { get; set; }
public List<StudentViewModel> AllStudents { get; set; }
}
In the View I would bind the Lesson properties using DisplayFor to show the details of the Lesson. Then I would have a DropDownListFor which uses AllStudents as its source. When a user selects a Student from the list a second DropDown would be generated (etc) allowing further Students to be added. These selections will be added to the Students property of my ViewModel.
In the past I would have handled this is javascript and managed the updating via JSON and AJAX. My instinct is that I should be using MVC Templates for this but in my research I've not found an example that has this exact scenario (or of course I may be barking up the wrong tree).
I have no code for my View at the moment as I'm really stuck on the right strategy to use in this case.
I hope that makes sense and any help on this greatly appreciated.
Many Thanks
Simon.

Your Lesson class should have a binding for Students, something like this:
Lesson.cs
public class Lesson
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Student> Students { get; set; }
}
Then in your Student class you would bind a relation to lessons:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Lesson> Lessons { get; set; }
}
Now you have a many-to-many relationship between Lesson and Student.
Your viewmodel would then look more like this:
public class ScheduleViewModel
{
public Lesson Lesson { get; set; }
// All students property
public List<Student> Students { get; set; }
}
Your Lesson property now contains all the information about your lesson, including which Students are connected to it, because it has a List of Students. You want to add the selected students from your view to this list.
The Students property is used to display all students in the database.

Related

Issue with navigation properties in EntityFramework 6

If I have a Course class, that has a collection of students (ICollection<Person>) as follows:
public class Person
{
public Person()
{
this.Courses = new HashSet<Course>();
}
public int PersonId { get; set; }
[Required]
public string PersonName { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
public Course()
{
this.Students = new HashSet<Person>();
}
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual ICollection<Person> Students { get; set; }
}
I end up with this structure in the database (as expected):
(note the PersonCourses table)
However, in my example, I also want to add an instructor to the course.
This instructor is also a Person, who can attend courses just like everyone else, so I adjust the above classes as shown below:
public class Person
{
public Person()
{
this.Courses = new HashSet<Course>();
}
public int PersonId { get; set; }
[Required]
public string PersonName { get; set; }
public virtual ICollection<Course> Courses { get; set; }
public virtual ICollection<Course> InstructedCourses { get; set; }
}
public class Course
{
public Course()
{
this.Students = new HashSet<Person>();
}
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual ICollection<Person> Students { get; set; }
public virtual Person Instructor { get; set; }
}
What I was expecting to see is the same database structure as above, but with an additional table created that linked a person to many courses.
However, what I got was this:
(Note that the PersonCourses table has gone)
What I was Expecting/Hoping to see was similar to this:
It's probably worth stating that the reason I've not got a separate Instructor/Person class is that I'm expecting that any Person can create a course, and thus become an instructor for that course.
Firstly - Is this possible to achieve via code-first in EF? I'm assuming so..
Secondly - What is it I'm doing wrong?
Thirdly - Is it the weekend yet?
All help appreciated :)
This is one reason I don't like / recommend code-first. It looks like EF got confused with the second InstructedCourses collection and instead just set up the instructor reference back from the course, though it seems to have just made the students collection a 1-to-many as well.
I would seriously consider either:
A) changing you domain to define an Instructor entity vs. Student entity
or
B) Do schema first with the proper EF mappings to the tables you want.
I don't think any DBA is going to want to see things like course_personId / Person_personId throughout the schema that they are one day going to need to support and optimize.
Instructors and Students can extend a base "Person" class with either table per entity or an identifier. Course to instructor and course to student relationships can then be defined more clearly. The limitation would be if you wanted the same "person" to be able to be referenced as both an instructor and a student.

How many to many relationship table do I draw data with code-first?

I have two tables in my student's name and course name. There is a lot of relationship between them. How do I draw the lessons of the learner? Below are the entities I wrote.
public class Student
{
public Student()
{
Lessons = new HashSet<Lesson>();
}
public int ID { get; set; }
public int MaxCredit { get; set; }
public int MemberID { get; set; }
public virtual Member Member { get;
public virtual ICollection<Lesson> Lessons { get; set; }
}
public class Lesson
{
public Lesson()
{
Students = new HashSet<Student>();
}
public int ID { get; set; }
public string Name { get; set; }
public int LessonCredit { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
I tried to get a students lessons with:
uow.LessonRepository.GetALL().Where(x=> x.Students.Any(s=> s.MemberID==id))
but it gave me a casting error.
Your EF model is correct, to do a many to many relationship both sides have a virtual ICollection<T> of the other type.
Your query isn't right though if you want to get a student's lessons, parsing it we start with a LessonRepository (presumably an IEnumerable<Lesson>) and then do a Where on it to filter the collection down; so far so good (a bit odd starting with the lessons, but whatever). The condition is (in English) where there are any students with a MemberId matching "id" (wherever that came from). Not exactly sure what that would give you, but its not a student's lessons.
Since you already have the navigation property, you can just access the Lessons property of a given student object. Assuming you started with a student id something like:
Students.FirstOrDefault(s => s.Id == id)?.Lessons;
FirstOrDefault searches for the correct student item and then you just access the Lessons property.

ViewModel Structure Decision

As my domain classes I have Person and FavoritePerson classes as follows.
public class CompanyPerson : ICompanyPerson
{
[Key]
public Guid PersonId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
public class CompanyFavoritePerson : IFavoritePerson
{
[Key]
public Guid FavoritePersonId { get; set; }
[Column(TypeName = "datetime2")]
public DateTime CreateDate { get; set; }
public Guid? CompanyPerson_PersonId { get; set; }
[StringLength(128)]
public string CompanyUser_UserId { get; set; }
public virtual CompanyPerson CompanyPerson { get; set; }
public virtual CompanyUser CompanyUser { get; set; }
}
In my web application I will need to show List of Favorite Person. So my view model is like this;
public class FavoritePersonViewModel
{
public Guid FavoritePersonId { get; set; }
public DateTime CreateDate { get; set; }
public Guid? CompanyPerson_PersonId { get; set; }
public string CompanyUser_UserId { get; set; }
//Option1: PersonViewModel PersonViewModel {get; set; }
//Option2: public string Title {get;set;}
}
Since I need to show Title of the favorite user in the list (where title belongs to Person class) which way will match with best practices?
Referencing a viewModel from another viewModel or extend viewModel with required extra attributes and fill them in business layer?
After some more research on this topic; I found out at this question
What is ViewModel in MVC?
it is clearly stated that:
View models can combine values from different database entities.
As like below;
So now you have data from the Employees and Departments tables in one
view model. You will just then need to add the following two
properties to your view model and populate it with data:
public int DepartmentId { get; set; }
public IEnumerable<Department> Departments { get; set; }
So I am going with Option 2.
The ViewModel pattern is just one of many patterns that fall into the 'Separated Presentation Pattern' bucket.
It's very important that you think about the requirements of your view before designing the ViewModel. For instance, if you have two widgets in your view and every widget has its own ViewModel, composite ViewModel is suitable in the situation, but if the view is just one that uses multiple domain classes, whether you have View model for each one, composite ViewModel is not suitable because it increases the complexity and every change in one ViewModel can break your code.
Thus, based upon your question
As my domain classes I have Person and FavoritePerson classes.
Since I need to show Title of the favorite user in the list (where title belongs to Person class).
It seems to me that composite ViewModel is not a good choice and you should design a new ViewModel.
It is also worth to read the ViewModel Best Practices

how to pass values from one view to another view

am very new to ASP.NET USING MVC Controllers...so here is my scenario.Am developing an Online Admission system where students come and fill in their required records to be processed for admission.I have different classes that holds specific information about the student.My student class is:
public class Student
{
public int ID { get; set; }
[Required]
public string NAME { get; set; }
[Required]
public string FIRSTNAME { get; set; }
[Required]
public string LASTNAME { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
I have another class called Enrollments where the user Enters all the courses as well as the Grade
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade Grade { get; set; }
public virtual Student Student
{
get;
set;
}
public virtual Course Course { get; set; }
}
public enum Grade
{
A,B,C,D,E,F
}
}
AND different more classes.I HAVE A CONTROLLER called Registration, where i will create the views for each Classes.
SO IN my first View which takes all the student Details,after the Record has been saved.
when the user Clicks next,i want to pass the ID number of the student class to my next view which is the Enrollment View and save the records along with the ID number of the student class which is acting as a foreign key in the enrollment class and database.
Have been trying so hard 2 implement this but with no success.I would appreciate a simple example to demonstrate how to achieve this because am not too good using the mvc framework as i recently started using ASP.NET.
First lets make it clear this is MVC and not web forms. In MVC the flow goes like this
View ----> Controller----->Model then back to controller and so on in usual cases.
Now what you probably want to do is pass a value(here ID ) from the form in first view to another view, this should probably be done like this.
Say for eg. your textbox storing ID is named "tb1" then write the following code in your controller
int id = (Request.Form["tb1"]).toString();
//now we'll store this id in a ViewState varibale like so
ViewData["id"] = id;
//don't worry about the data type of ViewData["id"], it would adapt automatically
Then after proper redirection, i.e. return of the second view from your controller, just access this ViewState variable any where you need like so:
#ViewData["id"]
Please note that this is Razor syntax and your views need to be in .cshtml pages rather than .aspx pages
You Can use ViewBag.MyProp = "XYZ";
and in our view you can retrieve this as :
var name = "#ViewBag.MyProp"

Create classes when reading from multiple tables in C#

I have tables in the database named
Students (StudentId,Name,Address)
Subjects (SubjectId,SubName)
Stud_Subjects (StudentId,SubjectId)
And I hv created c# classes for Student and Subject. I want to take data from Stud_Subjects table do i need to create another class named "Stud_Subject". How to add add properties to that class.
lets say I want to get data joining these tables and the result should be like this.
(StudentId,Name,SubName)
How to map these result into C# class. do i have to create another class with above three fields.
Two classes is enough. One for Student one for Subject. It looks like Stud_Subjects table is for "Many-Many relationship", you can implement that via a collection.
class Student
{
public int StudentId { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public List<Subject> Subjects { get; private set; }
}
class Subject
{
public int SubjectId { get; set; }
public string SubjectName { get; set; }
public List<Student> Students { get; private set; }
}
Subjects collection in Student class will have all the subjects which a student is mapped and Students collection in Subject class will have all the students which is mapped to current Subject.
You could also consider converting List<T> to Dictionary<int, Student> and Dictionary<int, Subject> for easy access them via their Ids

Categories

Resources