Updating database when using View Models - asp.net-mvc

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);
}
}

Related

MVC parameter not binding to controller action (KENDO UI)

Hope someone can help - this has been bugging me for around 2 hours - its probably something simple :)
Kendo UI Grid sends a request to my controller
http://localhost:1418/user/update?UserID=1&UserName=Admin&RoleName=Admin&Email=c.j.hannon%40gmail.com&Active=true&Company%5BCompanyID%5D=1&Company%5BCompanyName%5D=asd
However, the controller class 'Company' isnt bound by the binder? Can any one help my view model and controller action signature are below:
[HttpGet]
public JsonResult Update(UserViewModel model)
{
svcUser.UpdateUser(new UpdateUserRequest() {
UserID=model.UserID,
RoleID = model.RoleName,
Email = model.Email,
Active = model.Active.GetValueOrDefault(false),
UserName = model.UserName
});
return Json("", JsonRequestBehavior.AllowGet);
}
public class UserViewModel
{
public int UserID { get; set; }
public string UserName { get; set; }
public string RoleName { get; set; }
public string Email { get; set; }
public bool? Active { get; set; }
public CompanyViewModel Company { get; set; }
}
Cheers
Craig
A few things. Your immediate problem is that Company is mapped to a complex object not a primitive type. Kendo Grid just does not do this (as of this writing). Just guessing, but you probably want to setup a foreign key binding on the Grid and just pass back the Id of the company from a listbox. This is not as bad as you think and it will immediatly fix your problem and look nice too.
Maybe personal taste but seems to be a convention. Use the suffix ViewModel for the model that is bound to your View and just the suffix Model for your business objects. So a Kendo Grid is always populated with a Model.
Ex.:
public class UserModel
{
public int UserID { get; set; }
public string UserName { get; set; }
public string RoleName { get; set; }
public string Email { get; set; }
public bool? Active { get; set; }
public int CompanyID { get; set; }
}
public class CompanyModel
{
public int ID { get; set; }
public string Name { get; set; }
}
public class UserViewModel
{
public UserModel UserModel { get; set; }
public IList<CompanyModel> Companies { get; set; }
}
public ActionResult UserEdit(string id)
{
var model = new UserViewModel();
model.UserModel = load...
model.Companies = load list...
return View(model);
}
#model UserViewModel
...
column.ForeignKey(fk => fk.CompanyId, Model.Companies, "ID", "Name")
(Razor Notation)
BUT! This is just an example, you are better off Ajax loading the Grid with the IList becuase I assume you have many Users in the Grid at once, though you could server bind off the ViewModel with a List too. But the list of Companies is probably the same every time, so map it to the View just liek this rather than Ajax load it every time you do a row edit. (not always true)

ASP MVC, mutiple models with single controller / view with EF

Can someone explain to me how to use multiple models with a single view in which each of the models represent a DB table?
What I've currently done is created a model file for each model.
Example Model:
[Table("Order")]
public class OrderModel
{
[Key, Column(Order = 0)]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int OrderID { get; set; }
[Key, Column(Order = 1)]
public int UserID { get; set; }
public UserProfile Account { get; set; }
public DateTime Date { get; set; }
public int ShipLocation { get; set; }
public string PONumber { get; set; }
public int StatusID { get; set; }
public StatusModel Status { get; set; }
}
Here is the other model that encompases all the models to use in a single controller / view.
public class OrderPlacementModel
{
public OrderModel OrderChild { get; set; }
public OrderItemsModel OrderItemsChild { get; set; }
public StatusModel StatusChild { get; set; }
public MaterialsModel MaterialsChild { get; set; }
public CategoryModel CategoryChild { get; set; }
public PackModel PackChild { get; set; }
}
public ActionResult PlaceOrder()
{
var viewModel = new OrderPlacementModel
{
OrderChild = new OrderModel(),//or fetch this object from your data source
OrderItemsChild = new OrderItemsChild(),
//...etcetera
};
return View(viewModel);
}
Edit
Or, if you've strongly typed your view to a List<OrderPlacementModel> instead of a single instance, you could do something similar to this:
public ActionResult PlaceOrder()
{
var viewModel = new List<OrderPlacementModel>();
var model = new OrderPlacementModel
{
OrderChild = new OrderModel(),//or fetch this object from your data source
OrderItemsChild = new OrderItemsChild(),
//...etcetera
};
viewModel.Add(model);
//lather, rinse, repeat for however many instances you need to send to your view.
return View(viewModel);
}
Ideally, you should create a view model for the view that encompasses the fields from each model that you need to expose via the view. You can then map these in your controller. I would keep your mapping classes completely ignorant of your view models. Keep your views independent of your data model.
public class OrderViewModel
{
public int OrderId { get; set; }
public int UserId { get; set; }
public DateTime Date { get; set; }
public int ShippingLocation { get; set; }
public List<ItemViewModel> Items { get; set; }
}
public class ItemViewModel
{
public int ItemId { get; set; }
public int Title { get; set; }
}
Note how I have created a view model for the order and - to allow the order have multiple items - have separated these out into a separate model class. Now, you can type your view to OrderViewModel and use as many instances of ItemViewModel as your require.
You can then map your viewmodels to database entities from your controller:
[HttpPost]
public ActionResult ConfirmOrder (OrderViewModel model)
{
if (ModelState.IsValid)
{
foreach (ItemViewModel item in model.Items)
{
/* Create instance of OrderItemsModel (or whatever your
DB mapping class is), populate with appropriate data
from 'item' and commit to database. */
}
OrderModel order = new OrderModel();
order.OrderId = model.OrderId;
order.UserId = model.UserId;
order.Date = model.Date;
order.ShipLocation = model.ShippingLocation;
/* TODO: Commit new order to database */
}
}
Doing things this way adds a little overhead to your initial development time but allows you a great deal more flexibility as you aren't forced to mould all of your views to the shape of your entity classes.

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.

Display data from multiple models in one view

In the MVC 3 i want to display data from two models viz. Student and Enrollment into a single view.
Student model
public class Student
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int student_id { get; set; }
public string student_name { get; set; }
public string father { get; set; }
}
Enrollment model
public class Enrollment
{
[Key]
public int enrollment_id { get; set; }
public string rollno { get; set; }
public int student_id { get; set; }
public string registration_no { get; set; }
public date registration_date { get; set; }
}
My ViewModel looks like this
public class StudentEnrollmentViewModel
{
public Student_Info Student_Info { get; set; }
public Enrollment_Info Enrollment_Info { get; set; }
[Key]
public int ID { get; set; }
}
How do I retrieve the data from the two model and assign it to a viewmodel so that I can display it in a view? I am using Repository Design Pattern.
Generally speaking, a controller would be responsible for contacting the model, storing the result set returned in a variable/array/struc that the view would consume. The model and view components/classes would be registered in the controller.
An example:
<event-handler event="display.institutions" access="public">
<notify listener="userInstitutionRights" method="getInstitutionsWithDataRightsNOXML" resultKey="request.institutions" />
<view-page name="userNav" contentKey="request.userNav"/>
<view-page name="userInstitutions" contentKey="request.pageContent"/>
<announce event="assemblePage" />
</event-handler>
Event display.institutions is calling a model component userInstitutionRights and storing the result in a resultKey request.institutions and is including two view pages userNav, userInstitutions where the resultKey is available to each.
You can use DynamicPage, Look into following example
We need to use a Dynamic view page. (More Information)
Follow following steps:
Create DynamicViewPage type
public class DynamicViewPage : ViewPage
{
public new dynamic Model { get; private set; }
protected override void SetViewData(ViewDataDictionary viewData)
{
base.SetViewData(viewData);
Model = ViewData.Model;
}`
}
Your Controller will look like
public ActionResult Account(string returnUrl)
{
LoginModel loginmodel = null;//Initialize Model;
RegistrationModel registrationModel = null ;//Initialize Model;
// Any Extra logic
return View("Account", new
{
Login = loginmodel,
Register = registrationModel
});
}
your View should Inherit from
Inherits="DynamicViewPage"
Now #Model.Login will give you Loginmodel
#Model.Register will give you RegisterModel
It should work as you expected.........

asp.net mvc viewmodels. How much logic (if any) should they contain

I've been looking into view models for mvc and I'm looking for the best way to do them. I've read loads of different articles but none seem to be clear as the "best way." So far example I might have a Customer model with the following properties:
First Name
Last Name
Title
Location
Where location is a foreign key to a location table in the database.
I want to be able to edit this customer but only the first name, last name and location. I'm not bothered about the title in the edit. So in my view I will need to pass a customer and a selected list.
Now from what I've read I have the following options (there's probably many more).
So my question is basically which is the best one?
1)
Add a select list to the ViewData["Location"] and just create a strongly typed view of customer?
2)
Create a view model where I pass a customer and select list (the data access is done in the controller):
public class ViewModelTest
{
public Customer Customer { get; set; }
public SelectList Locations { get; set; }
public ViewModelTest(Customer customer, SelectList locations)
{
Customer = customer;
Locations = locations;
}
}
3)
Create a view model where I pass a customer and list of locations and create the select list in the view model.
public class ViewModelTest
{
public Customer Customer { get; set; }
public SelectList Locations { get; set; }
public ViewModelTest(Customer customer, List<Location> locations, string selectedLocation)
{
Customer = customer;
Locations = new SelectList(locations, "LocationID", "LocationName", selectedLocation);
}
}
4)
Pass a customer and repository and do the data access in the view model.
public class ViewModelTest
{
public Customer Customer { get; set; }
public SelectList Locations { get; set; }
public ViewModelTest(Customer customer, IRepository repository, string selectedLocation)
{
Customer = customer;
Locations = new SelectList(repository.GetLocations(), "LocationID", "LocationName", selectedLocation);
}
}
5)
Create the view model with just the properties I need:
public class ViewModelTest
{
public string FirstName { get; set; }
public string LastName { get; set; }
public SelectList Locations { get; set; }
public ViewModelTest(Customer customer, SelectList locations)
{
FirstName = customer.FirstName;
LastName = customer.LastName ;
Locations = locations;
}
}
6)
Or some other combination of the above or another way.
All opinions welcome.
Here's what I may suggest: have a view model which reflects the fields of strongly typed view:
public class SomeViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Location { get; set; }
public IEnumerable<SelectListItem> PossibleLocations { get; set; }
}
And in your controller action populate this view model:
public ActionResult Index()
{
var customer = Repository.GetCustomer();
var locations = Repository.GetLocations();
var viewModel = new SomeViewModel
{
FirstName = customer.FirstName,
LastName = customer.LastName,
Location = customer.Location,
PossibleLocations = new SelectList(locations, "LocationID", "LocationName", customer.Location);
};
return View(viewModel);
}
[HttpPost]
public ActionResult Index(SomeViewModel viewModel)
{
// TODO: Handle the form submission
return View(viewModel);
}
Of course doing the mapping between the model and the view model manually as shown my example could become quite cumbersome and in this case I would recommend you looking at AutoMapper.
I'd have my ViewModel as this
public class SomeViewModel
{
public Customer Customer { get; set; }
public IEnumerable<Location> PossibleLocations { get; set; }
}
My controller like this:
public ActionResult Index()
{
var viewModel = new SomeViewModel
{
Customer = Repository.GetCustomer(),
PossibleLocations = Repository.GetLocations()
};
return View(viewModel);
}
and then you can access everything in your Customer object in the view like this:
Customer name - <%: Model.Customer.FirstName %> <%: Model.Customer.LastName %>
Location - <%: Html.DropDownList("LocationID", new SelectList(Model.PossibleLocations as IEnumerable, "LocationID", "LocationName", Model.Location.LocationID))%>

Resources