MVC Complex Model Binding to List<T> - c#

I am a new noob at c# MVC and I would really like some help if possible or if someone could kindly point me in the right direction. I have spent hours and hours looking for a solution online and I am yet to find anything helpful which is why I am posting here.
I am working on creating an employee database web application for my company and I have for example the following classes.
namespace my_app.Models.Employee
{
public class Employee
{
public Employee()
{
}
// id
public int id { get; set; }
// employee id/payroll no
public string Employeeid { get; set; }
// employee qualifications
public IList<EmployeeQualification> EmployeeQualifications { get; set; }
}
}
namespace my_app.Models.Employee
{
public class EmployeeQualification
{
public EmployeeQualification()
{
}
// employee Qualification id
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
// employee Qualification institution
public string Institution { get; set; }
// employee Qualification qualificaiton
public string Qualification { get; set; }
public IList<Employee> Employees { get; set; }
}
}
The employee is being added to an EmployeeViewModel which looks something like this:
namespace my_app.ViewModels
{
public class EmployeeViewModel
{
public Employee Employee { get; set; }
// more stuff here
}
}
Now the problem I have and that I need help with is I would like to display the employee (i.e. Name, Address etc) in a view but also be able to list out the employee's qualifications with the ability to add and delete qualifications.
I have seen lots of tutorials out on the web of how to do this via AJAX and Entity framework SaveChanges method and i am sure that will work but the slightly complicated part with what i want to do is that i would like for the changes to remain on the client and only when the whole form including any changes to the employee object is submitted then the changes are persisted to the database.
Is there any way to do this with standard mvc controls or do i have to write a ton of JavaScript to save the changes to a local array of objects and then on the form submit append the additional form data.
I did write a whole bunch of jQuery to kind of get it to work but the issue is that I need to have this functionality on multiple views of the application and to have to write that much code each time does not seem smart.
Any help would be greatly appreciated, thanks.

Hope I can give you a direction to move forward.
Firstly, you need to change your Models design. Basically, 1 employee can have many qualifications and 1 qualification can owned by many employees. So you have many-to-many relationship here. Which means you need 3 models (Employee, EmployeeQualification, Qualification). The 2nd model will hold foreign keys to employee and qualification.
Next thing is UI, you want to keep all changes in UI before doing only 1 submit to persist data. That's actually a very good idea in term of user friendly system design. To do this, you just need to maintain a list of SelectedQualificationIds. Then keep that list in a hidden field, so after submitting, you can just load all qualitifications from DB, compare with the list, and remove/add qualification accordingly.

Related

Creating C# objects via MySQL

I would like to get help from you, please :-)
I'm thinking about good way for programmatically creating of classes in C# via MySQL database.
In my app I'm creating composite classes. For example Student, Classroom, Room (dormitory) and so on. Class Student contains properties Classroom and Room. ClassRoom is also related to another entities in database...
public class Student
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Classroom Classroom { get; set; }
public Room Room { get; set; }
}
public class Classroom
{
public int Id { get; set; }
public string Title { get; set; }
public byte Level { get; set; }
public Teacher Teacher { get; set; }
}
...etc
Usually when I need create object of some class, I must create also another classes' objects (sometime a part of database :-) ).
I think this way is not good optimalized, BUT there are great OOP benefits. When I load all students in a DataGridView, I can manage lots of related parts... for example:
Student student = ...
string currentTeacher = student.Classroom.Teacher.LastName //... and so on.
Is OK to create all related classes' objects immediately or is better create only necessary data of current created object and another data load / create "on demand"?
Or absolutely different way? :-)
See, the idea is for you to make a query exactly like what you need an Ingress for a structural database like sql, talking a lot about the EntityFrame mappings where it is possible to query only the student object/table only by its id, however, if no process you will need the Classroom in which it belongs to you use a .Include() no entity and you would only be able to fetch the objects you will need in your request no problem mapping all entities as a bank into objects, the problem is to retrieve all of them from the relation since it only has a feature in some articles
https://learn.microsoft.com/pt-br/ef/ef6/querying/related-data
https://www.entityframeworktutorial.net/code-first/configure-entity-mappings-using-fluent-api.aspx
But if your purpose is performance, mount only the data you need in an object, just an example StudentInfo with the information you need grouped, use a Dapper to make the optimized query and make it do this mapping to your object, gaining performance
this is clear speaking of relational databases like Sql if your case is a NoSql database like MongoDb there is no problem in your mappings since it will return everything in a single document it is structured for this type of information there will be no InnerJoin cost between tables

Model relationships causes trouble with Swagger/endpoints

I have two entities, Classroom, and Student. One-to-many relationships and I use EF Core: one Classroom can have many Students, and one Student can be in one Classroom.
public class Classroom
{
public int ClassroomId { get; set; }
public List<Student> Students{ get; set; }
}
public class Student
{
public int StudentId { get; set; }
public string Name { get; set;
public int ClassroomId { get; set; }
public Classroom Classroom { get; set; }
}
To make this as short as possible I have left out Controller, DataContext, DbSet<> for tables, etc., but I have created it all.
Now, if I run my application and I want to POST a Classroom through Swagger, it includes the list of students in the model schema, so that I have to create the list of students as if it was a property that I wanted to create at the same time as I create a classroom. Similarly, if I want to create/POST a student, it includes the classroom in the model schema.
Instead, I want to be able to create a Classroom and Student separately. When I create a Student, I want to be able to specify only the Classroom Id, so that I can connect students to existing classrooms, not create them same time.
How do I go about this? I am sure I misunderstand something, so I appreciate all help I can get. Thanks
How do I go about this?
You do not expose your database models to the outside world. They are your database models, not your data exchange objects. Nobody but your ORM needs to see them or should see them, you can annotate them so your database layer will know what to do.
Your controller layer should have a set of model classes that you use to talk to the outside world via REST (using swagger). You can build them however you see fit, you can annotate them so your REST layer will understand what to do.
Then you need to map the models you get from the outside world to something you want to do with your database. That should be quite straightforward.

C# MVC: How to create model for multi step form?

I have multi step form where user completes university registration:
In first form, student fills his personal details. (Name, Email, Mobile)
In second form, student fills marks obtained in high school. (Percentage)
In third form, student fills his parents details. (Name, Email, Mobile)
This finally gets saved to database.
I have created three partial views for these forms.
Should I create 1 c# model class that has all these fields and use it across the three partial views?
OR
Create separate c# model classes for each of these views?
Please note that these form steps may have different layout/number of fields on different platforms like desktop,mobile,apps i.e. we are planning to combine step1 and step2 on desktop and keep them as separate steps on mobile & apps.
Do something Like
public class ViewModel
{
public basic BDetails{get;set;}
public Advanced ADetails{get;set;}
public Personal PDetails{get;set;}
}
public class basic{
.....
}
public class Advanced
{
....
}
public class Personal
{
......
}
use the ViewModel class as your View class
Separate Models and Database Tables.
I did a similar project for a secondary school and the original system was based on flat topography. There ended up being multiple children and redundant information as siblings were enrolled. The data was partially normalized into tables for families, students, schools, subjects and bridge tables to handle the 1:n and n:n relationships between them.
While this took a little longer to implement, it was easier when the system was enhanced to be more user friendly. Schools were pre-populated and able to be selected in a list; the same was done with subjects. Addresses were standardized to conform with post office specifications.
The end result was a very happy client. Besides uniformity of the information, duplicate emails and postal mailings were heavily reduced. They also contracted for further work to add in demographic reporting and identify their recruiting weaknesses and strengths. They contracted with a sister company for marketing too.
First off: I would suggest that you use one model for student and parent(since they have similar properties - Name, Email, Mobile). You could make a Person model and it could have the enum PersonType. PersonType should be Student or Parent:
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Mobile { get; set; }
public PersonType Person { get; set; }
}
enum PersonType
{
Student,
Parent
}
Next off: only one view is necessary in your case. Use a ViewModel as the model for it. The ViewModel could look something like this:
public class VMStudentDetails
{
public float Percentage { get; set; }
public Person Student { get; set; }
public Person Parent { get; set; }
}

RavenDB Relational Model

I'm working on a side project to create a Forum built on top of RavenDB. I am currently trying to work out the relationship between Authors of Topics, and the "Last Reply" user on a topic. In a typical relational model I would simply store the FK to the User who posted the topic, and have a join off to the replies table to get the most recent replies author. This is obviously not the use case for Raven or any Document store for that matter.
What would be the most "optimal" way of pulling this off? Currently I'm tossing around a couple ideas.
Idea 1:
Save the FK of the Author on the Topic model, add a JsonIgnored User object that I will populate on the Topic load by using an Include in my session load (so one request so far from the client side, just makes the Load itself and model a bit complicated). Then possibly using a map-reduce index to get the most recent replies author (or even the same method as getting the Topic Author, so 1 or 2 queries depending).
Idea 2:
Saving both the Author and the most recent reply User on the model. Main "problem" here is the potential for stale data (say if a username changes). However that could potentially be alleviated with a background task (or simply keeping that in mind when updating a user document and going back over all posts from a user).
Example of the models in question.
public class User
{
public string Id { get; set; }
public string UserName { get; set; }
public string PasswordHash { get; set; }
}
public class Topic
{
public string Id { get; set; }
public string Title { get; set; }
public string Body { get; set; }
// Idea 1 Relationships
public string AuthorId { get; set; }
[JsonIgnore]
public User Author { get; set; } // Would need to be populated on loads from an Include on AuthorId
public string MostRecentReplyUserId { get; set; }
[JsonIgnore]
public User MostRecentReplyUser { get; set; } // Same as Author
// Idea 2 Relationships
public User Author { get; set; }
public User MostRecentReplyUser { get; set; }
}
Note: I would likely add a method to the User model to return a "clean" version where I scrub out things like the PasswordHash and use that on the Save for Idea 2.
Depending on your needs in case of update and query performance both ways may be the better choice.
I personally would recommend the first idea because you don't need to update existing documents when some data changes on the user records. Using include on query/load time is quite a nice feature of ravendb which can help you when retrieving nested records from the database.
Just make sure that you don't forget to include all nested documents - otherwise you may get many roundtrips.
Embedding documents (like Idea 1 but with a stored value of the users) may be better if your data processing is seperated from the data retrieval and you don't have access to the database session when converting data to be handed out to the frontend. We're using such a system that heavily relies on that (getting one input and mapping out a json pendant of the value) - this seperates data retrieval logic completely from the output (like mapping to json) logic.
Downside here: You've to make sure that existing (embedded) data get's updated whenever a user changes and the data that is transferred over the wire is more than on idea 1.

How to add new entity properties in Entity Framework without changing database model

I am new to Entity Framework. I started with database first approach which created my classes corresponding to the tables I selected. I am using MVC. One of my tables has a Date column in it. My requirement is I want to display the Day in my gridview(Grid MVC) i.e. if Date for that particular record fetched is 10/27/2015, then Day should show Tues. I want to do this without adding an extra column for the day in my database.
Is there a way for this. Any help will be greatly appreciated.
My model class generated is as below:-
public partial class QnsNew1
{
public int Id { get; set; }
public Nullable<System.DateTime> Date { get; set; }
public Nullable<int> PersonelID { get; set; }
public string CType { get; set; }
public string Comments { get; set; }
//public string Day { get; set; }//I want to avoid doing this
public virtual Personnel Personnel { get; set; }
public virtual Type Type { get; set; }
}
A better approach
Although this can be easily done with Entity Framework, but as pointed out by other answers here, and I acknowledge 100%, that it is better to separate presentation/UI models from database models by creating ViewModel separate from your regular entities and put your calculated properties there.
If you are stuck with EF, keep reading
In EntityFramework Database First mode, the classes generated for the database model are partial classes. You can basically create another partial class with the same name and in the same assembly and namespace and add your calculated property there.
here is a complete example (after borrowing the day of week implementation from the answers here):
public partial class MyTable
{
[NotMapped]
public string DayOfWeek
{
get
{
if (Date.HasValue)
return DateTime.Now.DayOfWeek.ToString();
else
return null;
}
}
}
It is important that you make sure the namespace is the same as the generated class. Also you will must annotate that property with NotMapped attribute so that EF does not attempt to map or save that property back to the database.
Create a partial class by the same name and have a getter property called Day. This will not add a column to your database.
public partial class QnsNew1
{
public string Day
{
get
{
if (Date.HasValue)
return DateTime.Now.DayOfWeek.ToString();
else
return null;
}
}
}
You could add a partial class QnsNew1 { public DayOfWeek Day { get; set; } } next to your generated data/model. But I would probably suggest that you separate your DataModel from your presentation model and this is a good reason why. Your presentation model will have parts to it that are just used for presentation and are computed on the fly whereas your data model should just strictly represent your data that is persisted.
Consider using a view model instead of model created by entity framework. I do not prefer using database models in my views because of the issue that you are facing. Instead I create a view model and then copy data from database model to view model using AutoMapper or some library like that.

Categories