MVC index method with change to viewmodel - asp.net-mvc

I want to know the best way of writing an index method in MVC when we need to make changes to the model before passing it to the view.
I picked up a technique from somewhere where you can pass in the model you intend to pass to your view, and make changes to it, but we have noticed that model binding kicks in when passing to the index method, and validation fires, when there is no need for it to because it is the initial load.
For example, which is more correct:
public ActionResult Index(ViewModel model)
{
model.SomeProperty = "MyNewValue";
return base.Index(model);
}
OR
public ActionResult Index()
{
ViewModel model = new ViewModel();
model.SomeProperty = "MyNewValue";
return base.Index(model);
}
and is there anything I should know about the implications of using either one?

With initial loads, the latter makes more sense. Controllers in MVC are newed up each and every single time you use them. There is no persistence whatsoever. With the first option, HTTP requests need some pre-existing knowledge of your API. So, the recommendation is to have the view model come from a persistence layer.
public ActionResult Index()
{
var vm = /* persistence layer */
vm.SomeProperty = "NewVal";
return View(vm);
}

Related

Update entity with ViewModel and Entity Framework 6?

I've been looking around and can't quite find the answer. I'm using a ViewModel in my Edit View so that I can have values for some dropdownlist. Now when I go to update my DB I'm not understanding how I can update my database record. I'm guessing I could create a new entity object, do a Find, and then update each property based on the ViewModel passed in from the Form but that sure seems like a lot of manual work.
Here I'm using the VeiwModel in the Edit View.
#model CPPCustomerCall.ViewModels.CustomerCallVM
Here is my controller's ActionResult. I changed the object type of the ActionResult to take in CustomerCallVM instead of the CustomerCall which was auto-generated. I assume since the Edit View's model is the ViewModel that's the type of object the ActionResult will receive. However, my ViewModel has more properties that aren't needed for the Entity Model to update the record. How do I go about updating my DB record in this ActionResult?
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "Id,CustomerName,Subject,PhoneNumber,CallMessage,CallDate,Status,CallNotes")] CustomerCallVM customerCall)
{
if (ModelState.IsValid)
{
db.Entry(customerCall).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(customerCall);
}
First, Bind and view models are mutually exclusive. If you don't want something to be eligible to be modified, then it shouldn't be on your view model, in the first place. Barring that, view models diverge from entities in the sense that they can't be saved directly. As a result, there's always some intervention present on your part to map the posted values back onto the entity, which means you can then selectively not map over certain properties that shouldn't be, regardless of whether they were posted or not. Long and short, get rid of the Bind stuff. It's just something else to maintain and a huge source of potential bugs.
That said, the code you have is workable; you're just missing the crucial part where you map the data from your view model back onto your entity. First, you need to fetch the entity from the database so you have a base to work from:
var customerCall = db.CustomerCalls.Find(id);
if (customerCall == null)
{
return new HttpNotFoundResult();
}
FWIW, your edit route should include the id in the route, according to REST conventions. Following REST isn't strictly required, but it's certainly recommended. While a web application adhering to REST doesn't mean it's a good application, not adhering to rest is generally a sure sign of a badly designed and coded application.
Then, you map over your properties. You can either do this manually:
customerCall.CustomerName = model.CustomerName;
// etc.
Or you can use a library like AutoMapper.
mapper.Map(model, customerCall);
AutoMapper requires a bit of initial setup to make this magic work, of course, so review the docs, if you're going that route. Manual mapping is easier, but far more tedious and repetitive.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "Id,CustomerName,Subject,PhoneNumber,CallMessage,CallDate,Status,CallNotes")] CustomerCallVM customerCall)
{
if (ModelState.IsValid)
{
// Find The record you need
var dbObj = CustomerCalls.FirstOrDefault(x=> x.id = customerCall.id);
//Check if null
if(dbObj == null) dbObj = new CustomerCall();
/// Map your properties
// Add object to the stack
if(dbObj.id == 0){
CustomerCalls.Add(dbObj);
}else{
CustomerCalls.Update(dbObj);
}
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(customerCall);
}

MVC Model Updating

I have a Model which is a business layer class and I pass that to the view through the controller in the following manner:
public ActionResult Edit(int id)
{
var MyModel = MyDatabaseInstance.Listings.GetByID(id);
return View(MyModel);
}
In the control for the update I have the following:
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
try
{
// TODO: Add update logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
What I really want is to be able to get back the model object that I have used during the initial binding. Is that possible? If I change the arguments of the edit as such:
public ActionResult Edit(Listing MyModel)
it complains that there is "No parameterless constructor defined for this object." and my model cannot have a parameterless constructor.
I would suggest that you use a ViewModel rather than binding directly to the entity from your database.
Using a ViewModel has the following advantages (not exhaustive).
Views often have specific requirements to how you display data. If you use the model from your DB then your going to endup adding unnecessary properties to your model.
Security, you don't want to expose properties on your model to automatic binding when posting back to the controller.
Validation requirements may be different for your view than your entity model.
Just easier to change a ViewModel if your presentation requirements change.

What is the right time for ViewData, ViewBag, Session, TempData

I was editing a project and I saw a Session[""] in one controller method and TempData[""] in another. Is there a difference between the 4 or is it just 4 ways to do the same thing.
ViewData/ViewBag - valid only for the duration of the current request. You set it in a controller action and use it in the view, then it disappears. The difference is that the first is a dictionary whereas the second is just a dynamic wrapper around this dictionary. Both point to the same data though. ViewBag was introduced in ASP.NET MVC 3.
Example:
public ActionResult Index()
{
ViewData["foo"] = "bar";
return View();
}
and inside the view you could use this value:
<div>#ViewData["foo"]</div>
Same with ViewBag but it is dynamic:
public ActionResult Index()
{
ViewBag.foo = "bar";
return View();
}
and inside the view you could use this value:
<div>#ViewBag.foo</div>
So as you can see ViewData/ViewBag are just an alternative way to pass information to a view from a controller action compared to the classic and recommended way which is using a view model:
public class MyViewModel
{
public string Foo { get; set; }
}
and then:
public ActionResult Index()
{
var model = new MyViewModel { Foo = "bar" };
return View(model);
}
and inside your strongly typed view:
#model MyViewModel
<div>#Html.DisplayFor(x => x.Foo)</div>
As you can see using view models provide a strongly typed approach in passing information to a view from a controller action.
TempData - it allows for persisting information for the duration of a single subsequent request. You store something inside TempData and then redirect. In the target controller action to which you redirected you could retrieve the value that was stored inside TempData.
Example:
public ActionResult Foo()
{
TempData["foo"] = "bar";
return RedirectToAction("bar");
}
public ActionResult Bar()
{
var value = TempData["foo"] as string;
// use the value here. If you need to pass it to the view you could
// use ViewData/ViewBag (I can't believe I said that but I will leave it for the moment)
return View();
}
ASP.NET MVC will automatically expire the value that was stored in TempData once you read it. Under the covers ASP.NET MVC persists the information into the Session.
Session - same as TempData except that it never expires - it will be valid for all requests, not a single redirect.
ASP.net MVC introduced ViewData, ViewBag, TempData, Session to pass data between controller to view.
ViewData
ViewData is implemented by using ViewDataDictionary class which stored in CurrentRequestContext. So, ViewData life-cycle will end when the current request ends.
ViewBag is also like ViewData, and only difference is it enable dynamically sharing the data using dynamics objects.
TempData is a very short-lived instance, and you should only use it during the current and the subsequent requests only.This will be handy if you want to use Redirections(RedirectToAction, RedirectToRoute, Redirect) in ASP.net MVC and pass some data among redirects. TempData stores data in Session but framework disposes the data when current and subsequent requests ends.
Session is long-lived(Never expires) data that belongs to user session.You need to be mindful when you use session variables which can be easily cause issues.
protected void Session_Start(Object sender, EventArgs e)
{
int userType = 1;
HttpContext.Current.Session.Add("_SessionUserType",userType );
}
ViewData:
Is a special dictionary inherited from ViewDataDictionary.
Used to send data from controller to view.
It's life span is the current request.
It will be destroyed if you have Redirect.
For security reasons, it's better to check it for null before usage.
The casting should be done for the operation.
ViewBag:
Is a dynamic type (this type is presented in c#4).
Like ViewData is used to send data from the controller to the view.
The duration of the validity of its values ​​in the current request.
In redirection between pages, its value will be null.
For security reasons before use, check it for null.
The casting is not necessary, so it's more faster than ViewData.
TempData:
A special kind of dictionary derived from TempDataDictionary.
It has Short life time, and used to send information between pages (Redirect).
After rendering the View completely, its value will be null.
For security reasons before use, check it for null.
The casting should be done for the operation.
Session:
used To send information between different requests.
Its value is not null not null values; Unless after a certain time (session expire).
For security reasons before use, check it for null.
The casting should be done for the operation.
This article explains the difference between ViewData, ViewBag and TempData. I hope you can refer this article for your need.

What is the purpose of passing a data model instance from within [HttpGet] Create to its View?

I notice there are 2 common practices to implement the Create form.
First Approach
From within [HttpGet] Create action method, we pass an instance of data model to the Create.cshtml as follows:
public ActionResult Create()
{
DataModel dm = new DataModel();
return View(dm);
}
Second Approach
From within [HttpGet] Create action method, we don't pass an instance of data model to the Create.cshtml as follows:
public ActionResult Create()
{
return View();
}
The [HttpPost] Create(DataModel dm) for both approaches is as follows:
[HttpPost]
public ActionResult Create(DataModel dm)
{
if (ModelState.IsValid)
{
db.Movies.Add(dm);
db.SaveChanges();
return RedirectToAction("Index");
}
else
return View(dm);
}
The question is: What is the purpose of passing a data model instance from within [HttpGet] Create to its View ?
Passing a data model to the view associated with the 'Create' is useful if you want the application logic to supply the initial values to be displayed on the form (whether because you don't want them hard-coded in the form defined in the view, or because they might differ depending on the context).
Default values for the bound controls, values in the viewmodel to be consumed by the view to generate dropdowns, etc... as mentioned by rsalmeidafl.
At the risk of sounding like a curmudgeon, this is really best practice. You shouldn't be calling the database to generate select lists and things from your views.
Finally, sending a default instance of the model to your view can also let you reuse edit/create views very easily, since you can bind values without fear of NullRef exceptions for your model. (if you strongly type your views)

Fetching data within an ASP.NET MVC ViewModel class?

For those that create ViewModels (for use by typed views) in ASP.NET MVC, do you prefer to fetch the data from a service/repository from within the ViewModel, or the controller classes?
For example, we started by having ViewModels essentially being DTOs and allowing our Controllers to fetch the data (grossly oversimplified example assumes that the user can only change employee name):
public class EmployeeViewModel
{
public String Name; //posted back
public int Num; //posted back
public IEnumerable<Dependent> Dependents; //static
public IEnumerable<Spouse> Spouses; //static
}
public class EmployeeController()
{
...
public ActionResult Employee(int empNum)
{
Models.EmployeeViewModel model = new Models.EmployeeViewModel();
model.Name = _empSvc.FetchEmployee(empNum).Name;
model.Num = empNum;
model.Dependents = _peopleSvc.FetchDependentsForView(empNum);
model.Spouses = _peopleSvc.FetchDependentsForView(empNum);
return View(model);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Employee(Models.EmployeeViewModel model)
{
if (!_empSvc.ValidateAndSaveName(model.Num, model.Name))
{
model.Dependents = _peopleSvc.FetchDependentsForView(model.Num);
model.Spouses = _peopleSvc.FetchDependentsForView(model.Num);
return View(model);
}
this.RedirectToAction(c => c.Index());
}
}
This all seemed fine until we started creating large views (40+ fields) with many drop downs and such. Since the screens would have a GET and POST action (with POST returning a view if there was a validation error), we'd be duplicating code and making ViewModels larger than they probably should be.
I'm thinking the alternative would be to Fetch the data via the Service within the ViewModel. My concern is that we'd then have some data populated from the ViewModel and some from the Controller (e.g. in the example above, Name would be populated from the Controller since it is a posted value, while Dependents and Spouses would be populated via some type of GetStaticData() function in the ViewModel).
Thoughts?
I encountered the same issue. I started creating classes for each action when the code got too big for the action methods. Yes you will have some data retrieval in classes and some in the controller methods. The alternative is to have all the data retrieval in classes, but half the classes you won't really need, they will have been created for consistency sake or have all the data retrieval in the controller methods, but again, some of those methods will be too complex and needed to have been abstracted into classes... so pick your poison. I would rather have a little inconsistency and have the right solution for the job.
As for putting behavior into the ViewModel, I don't, the point of the ViewModel is to be a thin class for setting and extracting values from the View.
There have been cases where I've put conversion methods in the ViewModel. For instance I need to convert the ViewModel to the corresponding entity or I need to load the ViewModel with data from the Entity.
To answer your question, I prefer to retrieve data from with in the controller/action methods.
Typically with DropDowns, I create a dropdown service. DropDowns tend to be the same data that spans views. With the dropdowns in a service I can use them on other views and/or Cache them.
Depending on the layout, 40 plus fields could create a cluttered view. Depending the type of data, I would try to span that many fields across multiple views with some sort of tabbed or wizard interface.
There's more than that ;-) You can fetch in model binder or action filter. For the second option, check Jimmy Bogard's blog somewhere around here. I personally do it in model binders. I use ViewModel like this: My custom ASP.NET MVC entity binding: is it a good solution?. It is processed by my custom model binder:
public object BindModel(ControllerContext c, BindingContext b)
{
var id = b.ValueProvider[b.ModelName]; // don't remember exact syntax
var repository = ServiceLocator.GetInstance(GetRepositoryType(b.ModelType));
var obj = repository.Get(id);
if (obj == null)
b.ModelState.AddModelError(b.ModelName, "Not found in database");
return obj;
}
public ActionResult Action(EntityViewModel<Order> order)
{
if (!ModelState.IsValid)
...;
}
You can also see an example of model binder doing repository access in S#arp Architecture.
As for static data in view models, I'm still exploring approaches. For example, you can have your view models remember the entities instead of lists, and
public class MyViewModel
{
public MyViewModel(Order order, IEmployeesSvc _svc)
{
}
public IList<Employee> GetEmployeesList()
{
return _svc.GetEmployeesFor(order.Number);
}
}
You decide how you inject _svc into ViewModel, but it's basically the same as you do for controller. Just beware that ViewModel is also created by MVC via parameterless constructor, so you either use ServiceLocator or extend MVC for ViewModel creation - for example, inside your custom model binder. Or you can use Jimmy Bogard's approach with AutoMapper which does also support IoC containers.
The common approach here is that whenever I see repetative code, I look to eliminate it. 100 controller actions doing domain-viewmodel marshalling plus repository lookup is a bad case. Single model binder doing it in generic way is a good one.
I wouldn't be fetching data from the database in your ViewModel. The ViewModel exists to promote separation of concerns (between your View and your Model). Tangling up persistance logic in there kind of defeats the purpose.
Luckily, the ASP.NET MVC framework gives us more integration points, specifically the ModelBinder.
I've got an implementation of a generic ModelBinder pulling information from the service layer at:-
http://www.iaingalloway.com/going-further-a-generic-servicelayer-modelbinder
It doesn't use a ViewModel, but that's easily fixed. It's by no means the only implementation. For a real-world project, you're probably better off with a less generic, more customised solution.
If you're diligent, your GET methods don't even need to know that the service layer exists.
The solution probably looks something like:-
Controller action method:-
public ActionResult Details(MyTypeIndexViewModel model)
{
if( ModelState.IsValid )
{
return View(model);
}
else
{
// Handle the case where the ModelState is invalid
// usually because they've requested MyType/Details/x
// and there's no matching MyType in the repository
// e.g. return RedirectToAction("Index")
}
}
ModelBinder:-
public object BindModel
(
ControllerContext controllerContext,
BindingContext bindingContext
)
{
// Get the Primary Key from the requestValueProvider.
// e.g. bindingContext.ValueProvider["id"]
int id = ...;
// Get an instance of your service layer via your
// favourite dependancy injection framework.
// Or grab the controller's copy e.g.
// (controllerContext.Controller as MyController).Service
IMyTypeService service = ...;
MyType myType = service.GetMyTypeById(id)
if (myType == null)
{
// handle the case where the PK has no matching MyType in the repository
// e.g. bindingContext.ModelState.AddModelError(...)
}
MyTypeIndexViewModel model = new MyTypeIndexViewModel(myType);
// If you've got more repository calls to make
// (e.g. populating extra fields on the model)
// you can do that here.
return model;
}
ViewModel:-
public class MyTypeIndexViewModel
{
public MyTypeIndexViewModel(MyType source)
{
// Bind all the properties of the ViewModel in here, or better
// inherit from e.g. MyTypeViewModel, bind all the properties
// shared between views in there and chain up base(source)
}
}
Build your service layer, and register your ModelBinder as normal.
Here's another solution: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/06/29/how-we-do-mvc-view-models.aspx
Main points there:
Mapping is done by a mediator - in this case it is AutoMapper but it can be your own class (though more to code). This keeps both Domain and ViewModel concentrated on the domain/presentation logic. The mediator (mapper) will contain (mostly automatic) logic for mapping, including injected services.
Mapping is applied automatically, all you do is tell the action filter the source/destination types - very clean.
(Seems to be important for you) AutoMapper supports nested mappings/types, so you can have your ViewModel combined of several independent view models, so that your "screen DTO" is not messy.
Like in this model:
public class WholeViewModel
{
public Part1ViewModel ModelPart1 { get; set; }
public Part2ViewModel ModelPart2 { get; set; }
}
you re-use mappings for specific parts of your View, and you don't write any new line of code, since there're already mappings for the partial view models.
If you don't want AutoMapper, you have have IViewModelMapper interfaces, and then your IoC container will help your action filter to find appropriate
container.Resolve(typeof(IViewModelMapper<>).MakeGenericType(mysourcetype, mydesttype))
and it will also provide any required external services to that mapper (this is possible with AutoMapper, too). But of course AutoMapper can do recursions and anyway, why write additional AutoMapper ;-)
Consider passing your services into the custom ViewModel on its constructor (ala Dependency Injection). That removes the model population code from your controller and allows it to focus on controlling the logical flow of the application. Custom ViewModels are an ideal place to abstract the preparation of things like SelectLists that your droplists will depend on.
Lots of code in the controller for things like retrieving data isn't considered a best practice. The controller's primary responsibility is to "control" the flow of the application.
Submitting this one late... Bounty is almost over. But...
Another mapper to look at is Automapper: http://www.codeplex.com/AutoMapper
And overview on how to use it: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/01/22/automapper-the-object-object-mapper.aspx
I really like it's syntax.
// place this somewhere in your globals, or base controller constructor
Mapper.CreateMap<Employee, EmployeeViewModel>();
Now, in your controller, I would use multiple viewmodels. This enforces DRY by allowing you to reuse those viewmodels elsewhere in your application. I would not bind them all to 1 viewmodel. I would refactor to something like:
public class EmployeeController()
{
private IEmployeeService _empSvc;
private ISpouseService _peopleSvc;
public EmployeeController(
IEmployeeService empSvc, ISpouseService peopleSvc)
{
// D.I. hard at work! Auto-wiring up our services. :)
_empSvc = empSvc;
_peopleSvc = peopleSvc;
// setup all ViewModels here that the controller would use
Mapper.CreateMap<Employee, EmployeeViewModel>();
Mapper.CreateMap<Spouse, SpouseViewModel>();
}
public ActionResult Employee(int empNum)
{
// really should have some validation here that reaches into the domain
//
var employeeViewModel =
Mapper.Map<Employee, EmployeeViewModel>(
_empSvc.FetchEmployee(empNum)
);
var spouseViewModel =
Mapper.Map<Spouses, SpousesViewModel>(
_peopleSvc.FetchSpouseByEmployeeID(empNum)
);
employeeViewModel.SpouseViewModel = spouseViewModel;
return View(employeeViewModel);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Employee(int id, FormCollection values)
{
try
{
// always post to an ID, which is the employeeID
var employee = _empSvc.FetchEmployee(id);
// and bind using the built-in UpdateModel helpers.
// this will throw an exception if someone is posting something
// they shouldn't be posting. :)
UpdateModel(employee);
// save employee here
this.RedirectToAction(c => c.Index());
}
catch
{
// check your domain model for any errors.
// check for any other type of exception.
// fail back to the employee screen
RedirectToAction(c => c.Employee(id));
}
}
}
I generally try to stay away from saving multiple entities on a controller action. Instead, I would refactor the employee domain object to have AddSpouse() and SaveSpouse() methods, that would take an object of Spouse. This concept is known as AggregateRoots, controlling all dependancies from the root - which is the Employee() object. But, that is just me.

Resources