How to implement ViewModels for asp.net MVC 5? - asp.net-mvc

I am new to asp.net, there are some questions on stack overflow but they don't fulfill my purpose. My question is..
How would I implement view model for the following two models?
public class model1
{
int student-id{ get;set}
string student-name{get; set;}
}
public class model2
{
int course-code{get; set;}
string course-name{get; set;}
}
Now I want to write a view model that could pass to a view and this view displays student-name and corresponding course-names.
Note: a student can enrolled in more than one course.

First of all you should modify your model. Student and courses have to be related. You can implement these relations like:
public class Student
{
public int Id { get; set }
public string Name { get; set; }
public ICollection<Course> Courses { get; set; }
}
public class Course
{
public int Code { get; set; }
public string Name{ get; set; }
public ICollection<Student> Students { get; set; }
}
After - you create view model. View model class must contain only what you actually need in you view. In you case - student and courses names. You can consider several options here. If you want just to display all course names in one line you can build you StudentViewModel like this:
public class StudentViewModel
{
public string Name { get; set; }
// In this case you can just join all courses' names to one string using string.Join(", ")
public string Courses { get; set; }
}
... or like this - if you want courses' names separated (to use them in some select or list html element). But you can create JoinedCources property which will return courses' names joined into one string.
public class StudentViewModel
{
public string Name { get; set; }
public ICollection<string> Courses { get; set; }
public string JoinedCources {
get {
return string.Join(", ", Courses);
}
}
}
Note: this is view model for only one student! If you want to display view which shows you the list of students and their courses you should either create new view model with property which is collection of StudentViewModel or in your view define model like #model ICollection<StudentViewModel> instead of #model StudentViewModel.
Now you have to map your model to view model. For example in your controller action when you get your student from database (or any other data source - file or web service):
public ActionResult StudentDetails(int studentId)
{
var student = _dataSource.GetStudent(studentId);
var model = AutoMapper.Mapper.Map<StudentViewModel>(student);
return View(model);
}
Now few words about mapping. AutoMapper is external class library you should definitely get to learn about if you want to work with view models and mapping in the future. It will help you simplify action method code and make it more readable. But since you're new to ASP. Net you can implement mapping by your self for the first time. For example like below:
public ActionResult StudentDetails(int studentId)
{
var student = _dataSource.GetStudent(studentId);
var model = new StudentViewModel()
{
Name = student.Name,
Courses = student.Courses.Select(c => c.Name)
}
return View(model);
}

public class StudentViewModel
{
public string Name{get;set;}
public int StudentId{ get;set}
public List<model2> Courses
}
You can consider combining the two models into the above view model if the goal is to display a student with course info.

Related

How to create a viewModel object for many-to-many relationships

I'm new to ASP.NET and currently working on a simple app to show a list of people and their hobbies. I have the following classes:
public class Hobby
{
public int HobbyID {get;set;}
public string Name {get;set;}
public string Type {get;set;}
public ICollection<PersonHobby> PersonHobbies {get;set;}
}
public class Person
{
public int PersonID {get;set;}
public int Age {get;set;}
public string Name {get;set;}
public ICollection<PersonHobby> PersonHobbies {get;set;}
}
public class PersonHobby
{
public int PersonHobbyID {get;set;}
public int PersonID {get;set;}
public int HobbyID {get;set;}
}
When viewing a person's Details page, I also need to display their hobbies. I did some research and found that ViewModels are a good way to accomplish this. So I created one, but I'm not sure if I did it correctly:
public class PersonHobbiesViewModel
{
public Person Person {get;set;}
public IEnumerable<PersonHobby> PersonHobbies {get;set;}
public IEnumerable<Hobby> Hobbies {get;set;}
}
And at this point I know that I need to create a viewmodel object in my controller's Details method and populate it with data, but I don't know how to navigate through the different tables. I have this:
public ActionResult Details(int? id)
{
var viewModel = new PersonHobbiesViewModel();
viewModel.Person = db.Person.find(id);
viewModel.Hobbies = ???
return View(viewModel);
}
On the other hand, if I'm going in the completely wrong direction, let me know! Thanks in advance.
Firstly what you might like to do, is change your entity models ever so slightly and let EF6 deal with the many to many complexity for you.
Your new model might look like this:
public class Hobby
{
public int HobbyID { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public Person Person { get; set; }
}
public class Person
{
public int PersonID { get; set; }
public int Age { get; set; }
public string Name { get; set; }
public ICollection<Hobby> Hobbies { get; set; }
}
Your context might be like:
public class MyContext : DbContext
{
public DbSet<Hobby> Hobbies { get; set; }
public DbSet<Person> People { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Hobby>()
.HasRequired<Person>(s => s.Person)
.WithMany(s => s.Hobbies)
.HasForeignKey(s => s.PersonId);
}
}
Then when you are creating your model you can simply query it like:
var person = db.People.Include(c => c.Hobbies).SingleOrDefault(x => x.PersonID == id);
var viewModel = new PersonHobbiesViewModel();
viewModel.Person = person;
viewModel.Hobbies = person.Hobbies;
return View(viewModel);
Have a read through the following tutorials about complex entity models and reading from them, they cover the same content.
Ps. I have not tested any of this.
#shenku's answer has correct way to map entities. But it is unnecessary if you don't consider about naming conventions.
Additionally Entity Framework has much of c#'s object oriented programming basics. That means if you pass the person object from your controller to view, you could access to your entities on view like below.
//Controller
var person = db.People.Include(c => c.Hobbies).SingleOrDefault(x => x.PersonID == id);
return View(person);
#*View Page*#
#using Project.Models
#* prints Person Name and Age *#
#Model.Name #Model.age
#* prints Hobby Names of Person
#foreach (var item in Model)
{
#item.name
}
You've answered your own problem correct as "circular reference" on your comment.
So you don't need PersonHobby class, Entity framework will automatically creates this table for you. This magic happens because you've defined hobby and person as a collection on their own classes.
Also you don't need a viewModel for your situation. Just pass the person or hobby object. Do not pass the Icollection because it's already loaded when you wrote db.People.Include(c => c.Hobbies). Also you can define classes as 'virtual' so it will load entities without include method.(Lazy-loading)

MVC: Correct way to add extra field to child object into view without making things complicated

I'm trying to do something i that feels like a small task, but i cannot figure out a simple way to do it. All my approaches for doing this gets really complex for a simple task.
I have these models:
public class Blog
{
public int Id { get; set; }
public String Name { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public class Post
{
public int Id { get; set; }
public String Name { get; set; }
public int BlogId { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int Id { get; set; }
public String CommentText { get; set; }
public int PostId { get; set; }
public int UserProfileUserId { get; set; }
}
public class UserProfile
{
public int UserId { get; set; }
public string UserName { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
}
In the Added Comments partial view, i want to show the full user name of the user that made a comment. If i just use my base classes in my views and partial views, i get everything i need except full user name on added comments. So far, i've thought of the following ways:
ViewModels - This will result in creating a ViewModel for each of my Classes and then populate / map them manually in my controller.
Code in Views - I have the UserProfileUserId so i can just ask the repository from the view but this Kills the MVC in MVC so i don't want to do it.
Actually Adding UserProfileFirstName and UserProfileLastName to the Comment Class as foreign keys - This feels like filling the database with view specific data. It doesn't belong in a relational database.
Using regular SQL and Query the database - Just because i know SQL, this -could- be a way to do it. but then again i'm killing the MVC in MVC.
How should i do this? Where is my silly overlooked option? I've searched a lot but could not find an answer, but this could be related to me not knowing all the technical terms yet. Sorry if this is answered 1000 times before.
Ideally i would change my domain model to include a Author property of type UserProfile and load that data as well using a JOIN (Comment table and User table)
public class Comment
{
public int Id { get; set; }
public String CommentText { get; set; }
public int PostId { get; set; }
public UserProfile Author { get; set; }
}
EDIT : As per the questions in the comment
This is how i will do this.
My Repositary method will have these methods
List<Comment> GetCommentsForPost(int postId);
BlogPost GetPost(int postId);
I would have ViewModel for representing a single blog post like this
public class PostViewModel
{
public int PostID { set;get;}
public string PostText { set;get;}
public string AuthorDisplayName { set;get;}
public List<CommentViewModel> Comments { set;get;}
public PostViewModel()
{
Comments=new List<CommentViewModel>();
}
}
public class CommentViewModel
{
public int CommentID {set;get;}
public string Text { set;get;}
public string AuthorDisplayName { set;get;}
}
Now in your GET Action, Get the data from your Repositary and Map that to ViewModel and send it to view
public ActionResult ViewPost(int id)
{
var post=repositary.GetPost(id);
if(post!=null)
{
PostViewModel vm=new PostViewModel { PostID=id };
vm.PostText=post.Name;
var comments=repo.GetCommentsForPost(id);
foreach(var item in comments)
{
vm.Comments.Add(new CommentViewModel { CommentID=item.Id,
AuthorDisplayName=item.Author.FirstName});
}
return View(vm);
}
return View("NotFound");
}
Now your view will be strongly typed to The PostViewModel
#model PostViewModel
<h2>#Model.PostText</h2>
#Html.Partial("Comments",Model.Comments)
And your partial view(Comments.cshtml) will be strongly typed to a collection of CommentViewModel
#model List<CommentViewModel>
#foreach(var item in Model)
{
<div>
#item.Text
<p>Written by #item.AuthorDisplayName</p>
</div>
}
Now our views are not depending directly to Domain models. This allows us to bring data from another source tomorrow if we need (Ex :Get comments from a web service) and simply map to our view model.
Some notes
Do not add too much of code to Views. Let's keep it pure HTML as much as possible. No data access calls directly from Views!
I manually mapped the domain model to viewmodel for your understanding. You may use a mapping library like Automapper to do so. Also you may move part of the code we have in the GET action method to another servier layer so that it can be reused in multiple places.

Updating database when using View Models

I have a question abou view models and adding information to a database.
Let's say i have these two classes:
public class Ad {
public int Id { get; set; }
public int CategoryId { get; set; }
public string Headline { get; set; }
public string Text { get; set; }
public int Type { get; set; }
public Category Category { get; set; }
}
public class Category {
public int CategoryId { get; set; }
public int CategoryName { get; set; }
public IColletion<Ad> Ads { get; set; }
}
Context class:
public DbSet<Ad> Ads { get; set; }
public DbSet<Category> Categories { get; set; }
The models are really over simpified but i just want to get a grasp of the context. Lets say i want to create a view model for the view that are suppose to add entries to the db. How do i go about adding info to the "Ads" database table from a view model. Lets say the view model looks something like:
namespace Website.Models
{
public class CreateViewModel
{
public Ad Ad { get; set; }
public ICollection<Categories> Categories { get; set; }
public Dictionary<int, string> AdTypes { get; set; }
public CreateViewModel()
{
// to populate a dropdown on the "Create" page
this.Adtypes= new Dictionary<int, string>
{
{1, "For sale"},
{2, "Want to buy"},
{3, "Want to trade"},
{4, "Have to offer"}
};
}
}
}
The only thing i really need when adding to the db is the parameters in the Ad class (although i need the view model to render the dropdowns). But how do I extract this from the CreateViewModel to add to the db.
This is my code at the moment:
[HttpPost]
public ActionResult Create(Ad ad)
{
if (ModelState.IsValid)
{
db.Ads.Add(ad);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(ad);
Since this is expecting a Ad class, how do i extract only the Ad paramaters from the view model and insert it to the db.
Sorry, very long post and probably some serious newbie stuff. I just didn't know how to explain it better.
I would appreciate if someone could explain about view models, or direct me to some site that does.
/m
You can use Viewmodels when you need more data on the website like values for dropdowns. So lets say you want to create a car.
Car object (Car.cs)
public class Car
{
public int Id {get;set;}
public string Color {get;set;}
public string Name {get;set;}
}
But you don't want to type color by yourself in a textbox. Let's say you want to pick color from dropdown. If so you need to add somehow list (SelectList) of colors to a dropdown.
Viewmodel is helpful in this situation (CreateCarViewModel.cs)
public CreateCarViewModel
{
public Car Car {get;set;}
public SelectList Colors{ get; set; } //List of colors for dropdown
}
Controller
ActionResult CreateCar()
{
CreateCarViewModel CCVM = new CreateCarViewModel();
List<string> colors = new List<string>{"Black","White"};
CCVM.Colors = new SelectList(colors);
//Your view is expecting CreateCarViewModel object so you have to pass it
return View(CCVM);
}
CreateCar (CreateCar.cshtml)
#model YourSolutionName.ModelsFolder.CreateCarViewModel
//form etc.
{
#Html.DropDownListFor(x => x.Car.Color, Model.Colors)
#Html.TextBoxFor(x => x.Car.Name)
}
Controller Again
[HttpPost]
//Again: but now controller expects CreateCarViewModel
ActionResult CreateCar(CreateCarViewModel CCVM)
{
if (ModelState.IsValid)
//update database with CCVM.Car object and redirect to some action or whatever you want to do
else
{
//populate your colors list again
List<string> colors = new List<string>{"Black","White"};
CCVM.Colors = new SelectList(colors);
return View (CCVM);
}
}

How should I map Edit Models to Domain Models

I'm trying to understand the best way to map the fields in an Edit Model (posted back from a view form) to a Domain Model so that I can SubmitChanges() and update the database record (via Linq to SQL). My example will be simple and the answer to it is probably to just do it manually. The question I'm asking is for models with many more fields where it could still be done manually and maybe that's the answer - but is there an easier way (using AutoMapper perhaps)?
My Domain Model:
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public int Color { get; set; }
public string SerialNumber { get; set; }
}
My View/Edit Model:
public class ProductVM
{
public int ProductId { get; set; }
public string Name { get; set; }
public IEnumberable<Color> Colors { get; set; } // To populate a dropdown in the view with color key/values
}
My Controller Action:
[HttpPost]
public ActionResult UpdateProduct(ProductVM product)
{
var productService = new ProductService();
productService.Update(product);
}
The Update method in my service layer:
public void Update(ProductVM productVM)
{
Product product = dataContext.Products.Single(p=>p.ProductId == productVM.ProductId);
// What's the best way to map the fields of productVM into product so I can submit the changes?
dataContext.SubmitChanges();
}
I just want to map the fields from productVM that match up with the fields in product. The Edit Model has fields that don't exist in the Domain Model and the Domain Model has fields that don't exist in the Edit Model. My ultimate goal is to update the fields in the database from the fields in the Edit Model.
Thanks
Use Automapper
public void Update(ProductVM productVM)
{
Product product = dataContext.Products.Single(p=>p.ProductId == productVM.ProductId);
AutoMapper.Mapper.DynamicMap<ProductVM, Product >(productVM, product );
dataContext.SubmitChanges();
}

How do I display data from multiple tables in a single MVC view

I am having a hard time solving the following with an MVC view.
My goal is to display data from multiple tables in a single MVC view. The bulk of the data comes from a table called Retailers. I also have another table called RetailerCategories which stores the retailerid from the Retailers table and also a categoryid linking to a Category table.
Note that there are multiple records for each retailerid in the RetailerCategories table.
In the view I want to show a list of retailers and with each retailer I want to show the list of categories applicable to them.
What would be the best way to accomplish this? Some of the things I have tried are covered in Can you help with this MVC ViewModel issue?
This however does not appear to be the right approach.
You need a view model specifically tailored to the needs of this view. When defining your view models you shouldn't be thinking in terms of tables. SQL tables have absolutely no meaning in a view. Think in terms of what information you need to show and define your view models accordingly. Then you could use AutoMapper to convert between your real models and the view model you have defined.
So forget about all you said about tables and focus on the following sentence:
In the view I want to show a list of
retailers and with each retailer I
want to show the list of categories
applicable to them.
This sentence is actually very good as it explains exactly what you need. So once you know what you need go ahead and modelize it:
public class CategoryViewModel
{
public string Name { get; set; }
}
public class RetailerViewModel
{
public IEnumerable<CategoryViewModel> Categories { get; set; }
}
Now you strongly type your view to IEnumerable<RetailerViewModel>. From here it is easy-peasy to do what you want in the view:
showing a list of retailers with each retail having a list of associated categories.
this could be also helpful;
video from chris pels
It is simple just do what I say step by step.
add connection string into web.config file
select models from solution explorer and add 4 classes as following
1st class for first table "i have employ table which have 3 columns
public class Employ
{
[Key]
public int Emp_id { get; set; }
public string Emp_name { get; set; }
public string Emp_city { get; set; }
}
2nd class for my tempo table
public class tempo
{
[Key]
public int ID { get; set; }
public int Emp_Id { get; set; }
public string subject { get; set; }
public string hobby { get; set; }
}
Now I create a third class in model folder which contain value that i want from employ table and tempo table
public class Alladd
{
public int ID { get; set; }
public int Emp_Id { get; set; }
public string subject { get; set; }
public string hobby { get; set; }
public string Emp_name { get; set; }
public string Emp_city { get; set; }
}
and the final class is datacontext class
public class DataContext:DbContext
{
public DataContext() : base("DefaultConn")//connection string
{
}
public DbSet<Employ> Empdata { get; set; }
public DbSet<tempo> Tempdata { get; set; }
}
now go to the Home controller and add code as below
public ActionResult file()
{
// IList<tempo> tempi=new List<tempo>();
IEnumerable<Alladd> model = null;
// model = getVerifydetails(id);
// return View(objcpModel);
List<Alladd> verify = new List<Alladd>();
cn.Open();
if (cn.State == ConnectionState.Open)
{
string query = "select Employ.Emp_name,Employ.Emp_id,Employ.Emp_city,tempo.hobby,tempo.id,tempo.subject from Employ inner join tempo on Employ.Emp_id=tempo.Emp_id;";//joining two table
SqlCommand cmd=new SqlCommand(query,cn);
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
verify.Add(new Alladd { Emp_name = dr[0].ToString(), Emp_Id= Convert.ToInt32(dr[1].ToString()), Emp_city = dr[2].ToString(), hobby = dr[3].ToString(),ID = Convert.ToInt32(dr[1].ToString()),subject= dr[4].ToString()});//filling values into Alladd class
}
cn.Close();
}
return View(verify);
}
now the final step is so simple
go to solution explorer
select views folder and left click on it and select add view
now name it as "file" which we give it into controller
check on create strongly type view
select model class from dropdown-> Alladd
select scaffold templet ->List
hit Add button
Now you're done
Happy coding...

Resources