I'm new to .Net development, and now are following NerdDinner tutorial. Just wondering if any of you would be able to tell me
What is the differences between ViewData
and ViewModel
(all I know is they are used to pass some form of data from controller to view) and perhaps tell me on what situation should I use ViewData instead of ViewModel and vice versa
Thanks in advance!
Sally
What is ViewData ?
dictionary object that you put data into, which then becomes
available to the view.
ViewData Sample
Controller Action method likes :
public class HomeController : Controller
{
public ActionResult Index()
{
var featuredProduct = new Product
{
Name = "Smart Phone",
QtyOnHand = 12
};
ViewData["FeaturedProduct"] = featuredProduct;
return View();
}
}
How to use ViewData on View ?
#{
var viewDataProduct = ViewData["FeaturedProduct"] as Product;
}
<div>
Today's Featured Product is!
<h3>#viewDataProduct.Name</h3>
</div>
What is a ViewModel ?
Allow you to shape multiple entities from one or more data models or
sources into a single object
Optimized for consumption and rendering by the view
Its like :
How to use ViewModel with MVC 3 ?
Domain Model
public class Product
{
public Product() { Id = Guid.NewGuid(); Created = DateTime.Now; }
public Guid Id { get; set; }
public string ProductName { get; set; }
}
ViewModel
public class ProductViewModel
{
public Guid VmId { get; set; }
[Required(ErrorMessage = "required")]
public string ProductName { get; set; }
}
Controller Action Method
[HttpGet]
public ActionResult AddProduct()
{
//for initialize viewmodel
var productViewModel = new ProductViewModel();
//assign values for viewmodel
productViewModel.ProductName = "Smart Phone";
//send viewmodel into UI (View)
return View("AddProduct", productViewModel);
}
View - AddProduct.cshtml
#model YourProject.ViewModels.ProductViewModel //set your viewmodel here
Conclusion
By using ViewModel can pass strongly-typed data into View
But ViewData is Loosely Typed.So Need to cast data on View
ViewModel can use for Complex scenarios such as merging more than one
domain model
But ViewData can be used only for simple scenarios like bring data
for the drop down list
ViewModel can use for attribute-based validation scenarios which
needed for Ui
But Cannot use ViewData for such kind of validations
As a best practices always try to use strongly typed data with
Views.ViewModel is the best candidate for that.
ViewData:
In short, use ViewData as support data, such as a datasource to a SelectList.
ViewModel:
ASP.NET MVC ViewModel Pattern
When a Controller class decides to render an HTML response back to a
client, it is responsible for
explicitly passing to the view
template all of the data needed to
render the response. View templates
should never perform any data
retrieval or application logic – and
should instead limit themselves to
only have rendering code that is
driven off of the model/data passed to
it by the controller.
[...]
When using [the "ViewModel"] pattern we create strongly-typed
classes that are optimized for our
specific view scenarios, and which
expose properties for the dynamic
values/content needed by our view
templates. Our controller classes can
then populate and pass these
view-optimized classes to our view
template to use. This enables
type-safety, compile-time checking,
and editor intellisense within view
templates.
Related
Given an ASP.NET Core MVC controller method with no parameters.
Given a view model is created and partially populated with third-party data within the controller method.
Given my C# models have properly defined data annotations for property validation.
Is it possible to leverage the ASP.NET Core MVC framework to validate my view model such that Html.ValidationSummary() shows errors from the pre-populated view model on initial viewing of the form (as opposed to only showing on the submission of the form)?
Controller:
public IActionResult ViewForm()
{
MyViewModel viewModel = new MyViewModel();
// Pre-populate view model with some data (which may be invalid).
viewModel.MyRequiredProperty = someThirdPartyDataSource.Value;
//I want to validate here so pre-populated data errors are shown
//in the views "Html.ValidationSummary()".
return View(viewModel);
}
Models:
public class MyViewModel
{
public MyObjectToValidate MyObject { get; set; }
}
public class MyObjectToValidate
{
[Key]
public int ID { get; set; }
[Required(ErrorMessage="Property is required.")]
public string MyRequiredProperty
}
Maybe I can... (couldn't find solid answers to these looking through documentation myself)
Manually bind and validate the viewModel somehow prior to calling return View(viewModel)?
Take advantage of ViewResult myViewResult = View(viewModel);?
ControllerBase (which all controllers implicitly inherit from) has a TryValidateModel method that should do exactly what you need. So:
public IActionResult ViewForm()
{
MyViewModel viewModel = new MyViewModel();
// Pre-populate view model with some data (which may be invalid).
viewModel.MyRequiredProperty = someThirdPartyDataSource.Value;
//I want to validate here so pre-populated data errors are shown
//in the views "Html.ValidationSummary()".
TryValidateModel(viewModel);
return View(viewModel);
}
I've been writing ASP.NET MVC applications for some time and I found them to be a good place for using the command pattern: we represent every user request as a command - a set of input params - then this command is processed (processing includes validation and other domain logic) and the result is sent back to the user.
Another thing I've been using in my applications is view models. I found them to be a more convenient way of passing data to the view than using domain objects as models or filling ViewData/ViewBag.
These 2 concepts work great for separating data that is shown to the user from user input and its handling, but they don't quite agree with each other in ASP.NET MVC.
Let's say I want to use commands and view models when developing a simple web store where users look through products and can order a product by providing their name and email address:
class ProductViewModel
{
public ProductViewModel(int id) { /* init */ }
public int Id { get; set; }
public string Name { get; set; }
// a LOT of other properties (let's say 50)
}
class OrderProductCommand
{
public int ProductId { get; set; }
[Required(ErrorMessage = "Name not specified")]
public string Name { get; set; }
[Required(ErrorMessage ="E-Mail not specified")]
public string Email { get; set; }
public CommandResult Process() { /* validate, save to DB, send email, etc. */ }
}
When looking through tutorials and SO I've seen people suggest several ways of doing this.
Option 1
Controller:
[HttpGet]
public ActionResult Product(int id)
{
return View(new ProductViewModel(id));
}
[HttpPost]
public ActionResult Product(OrderProductCommand command)
{
if (ModelState.IsValid)
{
var result = command.Process();
if(result.Success)
return View("ThankYou");
else
result.CopyErrorsToModelState(ModelState);
}
return Product(command.Id);
}
View:
#using (Html.BeginForm())
{
#Html.Hidden("ProductId", Model.Id)
#Html.TextBox("Name")
#Html.TextBox("Email")
<input type="submit" value="Place order" />
}
Pros: view model and command are separated from each other, the HttpPost method looks clean
Cons: I can't use convenient HTML helpers like #Html.TextBoxFor(model => model.Email), I can't use client validation (see my other question)
Option 2
We copy Id, Name and Email together with their validation attributes from command to viewModel.
Controller:
[HttpPost]
public ActionResult Product(ProductViewModel viewModel)
{
var command = new OrderProductCommand();
command.Id = viewModel.Id;
command.Name = viewModel.Name;
command.Email = viewModel.Email;
if (ModelState.IsValid)
// ...
}
View:
#Html.TextBoxFor(m => m.Email)
...
Pros: all of option 1 cons go away
Cons: copying of properties seems inconvenient (what if I have 50 of them?), validation of Name and Email in view model (it should be done in command where the rest of the domain logic resides), model as a POST parameter (see below)
Option 3
We make command a property of viewModel.
Controller:
[HttpPost]
public ActionResult Product(ProductViewModel viewModel)
{
var command = viewModel.Command;
if (ModelState.IsValid)
// ...
}
View:
#Html.TextBoxFor(m => m.Command.Email)
...
Pros: all of option 1 cons go away
Cons: view model should only contain data that is displayed to the user (and command is not displayed), model as POST parameter (see below)
--
What I don't like about options 2 and 3 is that we use a view model as a POST method parameter. This method is meant for handling user input (only 2 fields + 1 hidden in this case) and the model contains 50 more properties that I'll never use in this method and that will always be empty. Not to mention the necessity to create an empty constructor for the view model just to handle this POST request and the unnecessary memory consumption when creating large view model objects for every POST request.
My question is (that's like the longest question ever, I know): is there a secret Option 4 for properly using commands and view models that has all of the pros and none of the cons of the other ones? Or am I being paranoid and these cons are not that important and can be ignored?
Seems like the only other decent way go is to use a partial view for rendering the form and use OrderProductCommand as the view model.
Product.cshtml:
#model ProductViewModel
...
#Html.Partial("Product_OrderForm", new OrderProductCommand { ProductId = Model.Id })
...
Product_OrderForm.cshtml:
#model OrderProductCommand
...
#using (Html.BeginForm("Product", "Home"))
{
#Html.HiddenFor(cmd => cmd.ProductId)
#Html.TextBoxFor(cmd => cmd.Name)
#Html.TextBoxFor(cmd => cmd.Email)
<input type="submit" value="Place order" />
}
...
This way there is no need to create a data map between view models and business objects, and the controller code can be left clean as it was in in Option 1:
[HttpGet]
public ActionResult Product(int id)
{
return View(new ProductViewModel(id));
}
[HttpPost]
public ActionResult Product(OrderProductCommand command)
{
// process command...
}
Personally,
If I had to pass my model back to the view using a viewModel, I'd use option 4, inherit my view model from my command.
That way I get all the properties for my command, and I can set new properties that are just needed for the view, say dropdown list options etc.
Let inheritance do the work for you.
Also, you don't need to copy properties, in your post, don't send back a ViewModel, send back the command.
public ActionResult Product(PreOrderProductCommand command)
Don't forget, Mvc doesn't care what model is in your view, it only maps keys on the formcollection to properties in the model in the argument list. So even though you send a ProductViewModel out, you can still get a PreOrderProductCommand in.
HTH
Here's my take on the issue.
The reason we introduce all these layers (entity, view model, command etc.), which essentially represent the same concept within different domains, is to enforce separation of concerns.
However, as each layer is introduced, we increase the complexity and margin for error, due to increased mapping between objects and distributed validation.
In my opinion it is absolutely correct that entities and view models are implemented separately; a domain entity should represent the business logic, and shouldn't be polluted with UI specific features. Similarly, a view model shouldn't contain more than is necessary than to satisfy a specific view.
There is no reason, on the other hand, that commands should introduce a new layer to your architecture. A command simply needs to provide data, doesn't need to rely on a specific implementation, and therefore can be defined as an interface:
interface IOrderProductCommand
{
int ProductId { get; }
string Name { get; }
string Email { get; }
}
Whilst a view model isn't a command, or vice-versa, a view model could act as a command:
class ProductViewModel : IOrderProductCommand
{
public int ProductId { get; set; }
[Required(ErrorMessage = "Name not specified")]
public string Name { get; set; }
[Required(ErrorMessage ="E-Mail not specified")]
public string Email { get; set; }
public ProductViewModel(int id) { /* init */ }
// a LOT of other properties (let's say 50)
}
This way, the validation takes place only in the view model, which I think is the correct place for that to happen, as feedback can be given to the user immediately.
The command should simply transfer data, and the domain entity it mutates should validate itself anyway; a third layer of validation is unnecessary.
Your controller would then look as follows:
readonly CommandHandler _handler;
public YourController(CommandHandler handler)
{
_handler = handler;
}
[HttpGet]
public ActionResult Product(int id)
{
return View(new ProductViewModel(id));
}
[HttpPost]
public ActionResult Product(ProductViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
_handler.HandleProductCommand(model);
return RedirectToAction(nameof(Product), new { id = model.ProductId });
}
And the handler:
class CommandHandler
{
void HandleProductCommand(IOrderProductCommand command)
{
// Update domain...
}
// Other command handling methods...
}
Could you explain the interaction Models and ViewModels in the ASP.NET MVC?
If I need to display data on the page, but not edit, whether to create a ViewModel to display or use the Model?
I have two methods in the repository. One returns the Model and the other Model gets.In View I need to send the model. Should I convert the resulting Model to a ViewModel that would pass it to the View, and upon receipt of the submission to convert it back into the model to keep it?
For example I have a class model and class ViewModel.
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public int Price { get; set; }
}
public class EditItemItemViewModel
{
public string Name { get; set; }
public int Price { get; set; }
}
On the edit page, I clicked the edit item, and must get to the controller:
[HttpGet]
public ActionResult EditItem(int id)
{
//some code
return View();
}
Where can I get the ID if I passed in the view ViewModel in which there was no ID?
If I somehow got the ID, I need to do the following, which would save the model?
[HttpPost]
public ActionResult EditItem(EditItemItemViewModel ViewModel)
{
var Item = _dataManager.Items.GetItemById("1");
Item.Name = ViewModel.Name;
Item.Price = ViewModel.Price;
_dataManager.Items.AddItem(Item);
return View("Sucsess");
}
Could you tell me how to work with Models and ViewModels?
You can get the id a few different ways:
#Html.HiddenFor(m => m.Id)
This will include the property in the actual HTTP request.
Another option is to simply include the id as part of your route (this is what I usually do):
#Html.BeginForm("EditItem", "SomeController", new { Id = Model.Id }, FormMethod.Post) {
...
}
In both instances, make sure to validate that the user should be able to update the record that corresponds to that id! There's nothing stopping a user from tampering with the id and sending you a different value.
As for whether or not to display the database model or the view model, that's really up to you. I would always advocate building a view model and keep your database models out of the picture except for in your controller. The convention I use at work is for every database object that I need to send to users I will create a corresponding view model. So if I have a database object called Product I will build another class for the view called ProductModel.
An advantage of following that pattern is something I actually explained to another user earlier in regards to model binding.
If I need to display data on the page, but not edit, whether to create a ViewModel to display or use the Model?
If it's very simple (like your example) and the properties map 1-1 like they do now. I would just use the Model as a view model, it's easier. Though if you want you could create a view model with the exact same properties and populate that and display it..it's a bit more work, but makes it so your domain models aren't necessarily tied to your views.
If you do that you may want to look into something like AutoMapper which would allow you to do something like
//simply copies the properties from one type to another
var viewModel = Mapper.Map<Item, EditItemItemViewModel>();
I have two methods in the repository. One returns the Model and the other Model gets.In View I need to send the model. Should I convert the resulting Model to a ViewModel that would pass it to the View, and upon receipt of the submission to convert it back into the model to keep it?
If you go the view model route then yes, you will end up doing a lot of converting between model and viewmodel, that's where something like AutoMapper can help out, or you can just create a couple extension methods that convert between the two types.
It looks like Justin Helgerson's answer explaining a way to handle models/viewmodels is pretty good.
Your posting your viewmodel, so i'm going to assume that you've referenced your model within the page
#model MyNameSpace.EditItemItemViewModel
Is there a reason the id is not included in your view model? The easiest method would be to include that in the model and pass the instantiated model when creating the view.
[HttpGet]
public ActionResult EditItem(int id)
{
var myViewModel = new EditItemItemViewModel() { Id = id };
return View(myViewModel);
}
Like Justin said, it is easier to put it in a hidden field somewhere inside the #Html.BeginForm as the id should just be for reference.
[HttpPost]
public ActionResult EditItem(EditItemItemViewModel viewModel)
{
var Item = _dataManager.Items.GetItemById(viewModel.Id);
Item.Name = viewModel.Name;
Item.Price = viewModel.Price;
// Should this be an Add?
_dataManager.Items.AddItem(Item);
return View("Success");
}
I am quite new to MVC, and at the moment I'm developing my first application, and trying to get my head around it. For a while I have tried to find out the best way to use a DropDownList in MVC.
By that I mean, is it possible to modify a model, or viewmodel property in a way that the view that is strongly typed to it is passed a DropDownList with the desired options?
Basically what I am asking is, what is the best way to populate an MVC DropDownList with data from a db and send it to the view?
The list-items in an MVC DropDownList (a <select> element) are provided by any IEnumerable<SelectListItem> which is provided by your ViewModel or optionally through the ViewData, like so:
ViewModel:
public class CustomerViewModel {
public String Title { get; set; }
public IEnumerable<String> ValidTitles { get; set; }
}
Controller action:
[HttpGet]
public ActionResult AddCustomer() {
CustomerViewModel vm = new CustomerViewModel();
vm.Title = "Mr";
vm.ValidTitles = new SelectListItem[] { new SelectListItem("Mr", "Mr"), new SelectListItem("Mrs.", "Mrs."), new SelectListItem("Sgt.", "Sgt.") };
return View( vm );
}
View file (aspx syntax):
<p>Title: <%= Html.DropDownListFor( vm => vm.Title, Model.ValidTitles ) %></p>
So if you read and understand my example, you'll see where you should hit the database.
Remember: the ViewModel is an encapsulation of the entire variable-data state of a view passed to the client.
Basically what I am asking, what is the best way to populate a mvc dropdownlist with data from a db and send it to the view?
It is actually pretty simple,you just need to read the basics of how to use dropdowns in MVC. In a few words:
Create a model for passing it to the view, and let the controller build that model from the data incoming from db.
Create 2 properties in your model for using them in the dropdownlist, (i.e: int selectedItem, SelectListItem items)
Once in your view, note that there are 2 versions of dropdownlist helpers, the strongly typed and non-strongly typed, you should use the strongly typed always you can, it is better and more consistent.
We are using a friendly Name URL route scheme. Basically using a combination of the Principal Identity and a friendly name this can be mapped back to an identity internally (Person ID). So a route like this:
routes.MapRoute(string.Empty, "{friendlyname}/Products/{action}", new {controller = "Products", action = "List"});
Would map to a URL like this:
Adam/Products/List
This all works fine and abstracts away the internal Id of the named person which is required as well.
The problem is our Views are comprised of many partial views. When there are rendered by using the #Html.Action method they ultimately need the PersonID but from the URL we only have the 'friendly name'.
I have thought about this for a while and there are two solutions to my mind:
Pass the 'friendly name' into each of the controller action methods that return the partial views and internally the method will have to do a lookup on the currently logged in identity and the friendly name. This will give the PersonID to me and I can then efficiently query from then on. The only problem with this apporach is that due to the multiple partial views I will be querying on the currently logged in identity and friendly name for each partial view call which is innefficeint and I feel I should only have to write this code once.
Somehow query in the view and get the PersonID so it can be passed to each #Html.Action call so the partial view controller methods will not have to do that lookup themselves saving round trips to the database for the same shared informtion. The problem with this is that I am not sure of a way of doing this cleanley in the view using the DI that we use through the rest of the application.
Any thoughts on approach to would be greatly appreciated.
Thank you,
Adam
You could add the Id to the session variables and access it from within the views with:
#{var personId = int.Parse(Session["PersonId"])}
Then you can pass it directly to partial views from the Parent without it hitting the client or having to pass parameters to any controllers.
Update
You could also access the session variable from the Controller if you wanted to do the work there instead without roundtripping to the database.
EDIT
If you put the property in a model and pass it to a page that post's back then the model will not persist between posts.
If for example your controller does:
[HttpPost]
public ActionResult DoSomething(ViewModel model)
{
if(ModelState.IsValid)
{
// Logic Here
}
return View(model)
}
when the page is reloaded, the model will have forgotten about the ID.
There are a couple of ways around this. Either use #Html.HiddenFor(m => m.ID)
which will put the property in the rendered HTML, which if it is truely a sensitive piece of information, is bad.
Or you can rebuild the view model on each subsequent postback.
Hope this helps
As Marc states I could you the Session to deal with this but I have gone with using the Model as he has stated in his update. If the parent View Controller action takes in the friendly name it can do the lookup, put the PersonID into the model and then any Partial Renders can have the models value passed into them in the parent View Controllers Action's view. An example is shown below (this is demo code but it hopefully gets the point across, I would never use a static data context in real code)
Home controller
public class HomeController : Controller
{
public ActionResult Index(string friendlyName)
{
int pupilId = Data.People.Single(x => x.Name == friendlyName).PersonId;
HomeIndexViewModel homeIndexViewModel = new HomeIndexViewModel {PupilId = pupilId};
return View(homeIndexViewModel);
}
}
Home Index View
#model SharingInformationBetweenPartials.Web.Models.HomeIndexViewModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#Html.Action("DisplayPersonDetail", "Person", new {Model.PersonId})
The PersonController's DisplayPersonDetail method can then present the respective data it wishes using the passed in PersonId:
public class PupilController : Controller
{
[ChildActionOnly]
public ActionResult DisplayPupilDetail(int pupilId)
{
Person person = Data.People.Single(x => x.PersonId == pupilId);
return View(person);
}
}
I did try what I thought was this before but I must have got something wrong as the ViewModels properties were getting shown in the URL which is what I was trying to get away from. Anyway, I hope this helps anyone else who may be looking to do something similar. If you have any questions then let me know.
Thanks,
Adam
You can use [Bind] attribute to specify the exact properties a model binder should include in binding or use Exclude parameter on the attribute to exclude PersonId
[HttpPost]
public ActionResult EditPerson([Bind(Exclude = "PersonId")] Person person)
{
//do domething
}
You can also use [ReadOnly] attribute that model binder will understand and not assign to that property.
[ReadOnly(true)]
public int PersonId{ get; set; }
But the best approach is to use separate ViewModels: one only for viewing and one for editing.
public abstract class Person
{
public string Name { get; set; }
public string Surname { get; set; }
}
public class PersonCreateVM : Person
{
//no PersonId here
}
public class PersonEditVM : Person
{
public int PersonId{ get; set; }
}
This approach is maybe a "overkill" but when used properly and with AutoMapper http://automapper.codeplex.com/ it's an ease to work with.