I have started using MVC 3 and I really like working with it. It's a much better experience than WebForms on so many levels.
However I have a question about the ViewBag (the new ViewData)
I am very careful to stick to sound modeling practices where I tend to put everything my view needs into ViewModels. However as far as the ViewBag goes, are there any concerns other than poor modeling practice that suggests that ViewBag should be uses sparingly? I don't use it much myself, but a team mate asked the question today, and I could only recommend to limit its use since it's a weakly typed model,hacked to be cool using dynamic (sudo typed)
Form what I can tell there shouldn't be any performance impact from using it though?
Am I right? It's just another object that get's applied to the view server-side.
I am not counting any performance impact of using dynamic (if there is one that can be measured)
What are your views on disadvantages (or even advantages) of using the ViewBag?
For me, the big problem is that ViewBag isn't type-safe and can easily produce run-time errors if you aren't careful.
A view bag is really just a convenience to prevent simple cases where you would otherwise need a new viewmodel. I routinely use them, but only for very simple data.
from my post at:
http://completedevelopment.blogspot.com/2011/12/stop-using-viewbag-in-most-places.html
Even the built in templates give us support for ViewBag. The scaffolding templates create ViewBag.SomeEnumerable to use for our pages.
Sure - its quick code and since it's auto-generated will likely work ok. The problem comes in from the fact that ViewBag is a dynamic object. There are no compile time type checks. If you misspell something you won't be notified from the compiler.
I've seen this used in such bad ways, I would love to see it's usage curbed and applications cleaned up.
So in a controller:
ViewBag.SomeProperty = "10"
and in the view:
#ViewBag.Som3Pr0p3rty
We won't ever know of this error. It won't even generate a runtime error since the misspelled name just generated a null.
Use ViewModels and save yourself from these potential problems. Our templates set a page title in ViewBag as well.
There are several options in setting a page title. Since this is a well known property one could argue that using it just for Title is ok. I wouldn't argue this. There are other options.
1. Set it in a section in the layout and render it from the client.
2. Use ViewBag.Title
3. Use a filter (seems much too complicated for a title)
4. Use a Model.Title field.
Since by default we have a ViewBag.Title field created and our templates also get it by default, I'll yield that in this case, its ok.
What about select lists?
Rather than the default
ViewBag.CustomerId = new SelectList(db.Customers, "CustomerId", "FirstName", order.CustomerId);
Do something like
yourViewModel.Customers = customers; //a loaded collection
and in your view
#Html.DropDownListFor(x => x.CustomerId, new SelectList(Model.Customers, "CustomerId", "Name"))
Or if you prefer to set your ViewModel to contain the SelectList
yourViewModel.Customers = new SelectList(db.Customers, "CustomerId", "Name", order.CustomerId);
and now your view would be slightly cleaner as:
#Html.DropDownListFor(x => x.CustomerId, x.Customers)
See, that's not so difficult now is it? Enjoy!
Related
I've inherited a code base written in ASP.Net MVC 4. Every post method takes a FormCollection. Aside from annoyance of having to access the values through quoted strings, it also leads to drawbacks such as not being able to use things like ModelState.IsValid, or [AllowHtml] attributes on my ViewModel properties. They actually did create ViewModel classes for each of their views, (though they are pretty much just direct wrappers around the actual Entity Framework Model classes), but they are only used for the GET methods.
Is there anything I'm missing about FormCollection that gives a reason why this may have actually been a good idea? It seems to only have drawbacks. I'd like to go through and "fix" it by using ViewModels instead. This would take a good bit of work because the ViewModels have properties that are interfaces and not concrete classes, which means either writing a custom binder or changing the ViewModels.
But perhaps there's something I'm missing where it makes sense to use FormCollection?
Is there any good reason to use FormCollection instead of ViewModel?
No. I have following issues.
Issue - 1
In case FormCollection is being used...It will be mandatory to Type Cast the Primitive Type Values un-necessarily because while getting the entry of specific Index of the System.Collections.Specialized.NameValueCollection, value being returned is of type String. This situation will not come in case of Strongly Typed View-Models.
Issue - 2
When you submit the form and goes to Post Action Method, and View-Model as Parameter exists in the Action method, you have the provision to send back the Posted Values to you View. Otherwise, write the code again to send back via TempData/ViewData/ViewBag
View-Models are normal classes, created to bind data to-from Views
Issue - 3
We have Data Annotations that can be implemented in View Model or Custom Validations.
ASP.Net MVC simplifies model validatons using Data Annotation. Data Annotations are attributes thyat are applied over properties. We can create custom validation Attribute by inheriting the built-in Validation Attribute class.
Issue - 4
Example you have the following HTML
<input type="text" name="textBox1" value="harsha" customAttr1 = "MyValue" />
Question : How can we access the value of customAttr1 from the above eg from inside the controller
Answer : When a form get posted only the name and value of elements are posted back to the server.
Alternatives : Use a bit of jQuery to get the custom attribute values, and post that along with the form values to action method
Another option is to rather put what you got in your custom attributes in hidden controls
That's the reason, I would always prefer to use View-Models
The only advantage I can think of is if you want to use the automatically generated controller provided when you don't specify a EF model to be strongly typed to. In that case, your Create and Edit actions will use the FormCollection object as it is a reliable, pre-existing artifact of the framework to work with for this purpose. Perhaps the previous developer chose this option while creating his controllers, and stuck with it since Visual Studio must know what it's doing :)
But, in reality, I would never recommend this headstart of a few seconds. It's always better to build out viewmodels, I would recommend looking at the effort to move in that direction if only for maintenance purposes. With model binding and strongly typed views and html helpers, you are much more likely to reduce the number of run-time errors as a result of changing some magic string and not realizing it until your page blows up.
Ok, I see the general consensus here is that it isn't liked. To offer another perspective, I've always liked using the formcollection passed into the controller on POST actions. It offers the use of the TryUpdateModel method from the controller which will map the collection to your strongly typed class. TryUpdateModel also has overloads that allow you to white list the properties of the model that you want to allow to be updated.
if (TryUpdateModel(viewModel, new string[] { "Name" }))
{
//Do something
}
It still allows all the model binding you want, but helps to keep anything other than the "Name" property on my viewmodel from being updated.
You can see more about the TryUpdateModel method here:
http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.tryupdatemodel(v=vs.108).aspx
There are always workarounds for getting away from a FormCollection lol.. you can have hidden fields bound to your view model variables in the form to your heart's content.
Form collections mostly emerge from the laziness of creating a view model but still end up taking time trying to get figure out how to get the values out of it in your controller :P
I think it was simply created in the very beginning of MVC as an alternative to using strongly typed views when having very simple forms - back in the days when everyone used ViewBag :) ... and once hey had it in there they couldn't just take it out as simple as that.
Maybe you can use it if you are absolutely sure your view will never have more than one form input? Probably still a bad idea though..
I cant find any recent articles talking about any advantages of form collections.. while strongly typed views are everywhere.
Yes. Sometimes, it can be useful. Here's an example:
Let's say we have in our db "date_and_time_field".
In Razor View, we want to use two form fields. The first one "Date" (maybe with jQuery UI Datepicker). The second one "Hour".
In the Controller Action, we compose the "date_and_time_field" by means of Request.Form["Date"] and Request.Form["Hour"].
There are other scenarios where it can be useful:
A cross-table (with checkBoxes in Razor view)
The collection Request.Unvalidated().Form (maybe this is not part of your question: I don't wanna be off-topic)
The default model binder will do almost everything you need it to do. I resorted to the FormCollection once - only to later figure out how to bind arrays of elements into a collection on the ViewModel.
Just go ViewModel. Better all around, for every reason enumerated.
With form collection you will be able to get all the values inside the form. There can be situations where you may need to pass some additional values from the form which may not be part of your view model.
Just take an example of passing 10 hidden values from the form. The form collection makes sense.
The only difficulty that you may face is type casting. All form collection items that you get will be string; you may need to type cast based on your requirement.
Also model state validation is another area where you may face a challenge.
You can always add the form collection properties to your method signatures. They will automatically be populated by form values with corresponding keys.
Well with Forms Collection you will find a quick way to get the values of a form. Otherwise you have to create a class that mimics the Form Fields and people are sometime lazy to create custom classes for less important/rarely used Forms.
No there is no extra benefit (in fact limited) of forms collection over a custom class as action parameters and it should be avoided whenever possible.
Responding to the title question: yes.
There are some situations that FormCollection needs to be used. For instance, suppose a ViewModel that has a property that implements the 1 to N relation (in concrete case, a TimesheetViewModel with ICollection<TimesheetEntryViewModel>), and the Controller has to perform a validation between the time entries to not get a time collision between the end time of an entry and the start time of the following entry. To mark a related entry with a validation error, how can be the line index be retrieved?
Well, with the default model binding, the index value is lost in the Controller logic. Fortunately, FormController stores the index you used in the View and a more specific validation can be done.
There are type of SPA apps where you have no idea about your model (there is no ViewModel at all and views are created dynamically (for short ;))), so FormCollection is your only choice where you implement custom post validation having entire page input values...
If your view has a knowledge about the model then, of course, you can use your concrete ViewModel object. That's easy ;)
I know that you want to keep logic out of your views. I can elimate most loops by using DisplayFor/EditorFor and passing IEnumerables to the view.
What about IF statements? should they be avoided completely in views? used sparingly? as a last resort?
Say you wanted to show hide an element based on a User role...How would u go about doing this without an IF statement...a completely seperate view perhaps?
Just trying to get an idea of best practices.
Thanks!
Be consistent, and keep in mind the purpose of the view - to produce your HTML. Toward that end, certainly you will need some if constructs here or there. I think some people are suggesting you stick to some pie-in-the-sky, ultra-nitpicky purism here at the expense of usable, functional, well-defined code.
There is nothing wrong with using ifs in your views, as long as you don't end up putting backend logic in them.
Rob Conery has a rule of thumb that states "if there's and IF, make a helper". Personally, I would say "use sparingly". I avoid it as much as possible because it makes things more difficult to unit test.
For the situation where you want to hide elements based on user roles: For simple scenarios, I would probably put the check directly in the view. Usually I try to still make these more terse and testable, though. So instead of:
#if (HttpContext.Current.User.IsInRole("admin")
{
// Show admin stuff
}
I would do something like:
#if (Model.UserIsAdmin)
{
// Show admin stuff
}
On the other hand, if these kinds of checks started getting speckled all over your views, I'd probably create the elements conditionally in the viewmodel first, and then just display what's been built. Hope that helps.
Basically every View should display whats passed in ViewModel. If ViewModel is not enough, then I would look for a way of improving the ViewModel creation itself, not the View's logic.
All conditionals CAN be evaluated upon ViewModel creation.
Of course, It all depends on how much custom-logic in View does Your project organization tolerates.
I think is the better to avoid if in the view, You should avoid business(Model) or application(Controller) logic in the view
in your example you can create different Partial View for display some think depend on user role and in the Controller put the logic for what view you need to show
If / else can be used sparingly, in my opinion, but for the example you mention, hide an element based on role - the if check should definitely not be in the view. Write extensions and helpers where needed.
I would suggest to hide element by "if" in the view, but in code you must disable function (method), which is activated by hidden element.
I guess this is a story of how frameworks cheerfully do 95% of what you need, but then frown disapprovingly at that final five percent; inform you that if you want to participate in nonstandard malarky it's your own business, thank you very much, and it'll be here if you decide you want to return to doing things it's good at. Generally speaking it's inevitable that this final five percent will contain some version of a must-have feature.
I have a strongly-typed view which updates a data object. I've used idiomatic MVC2 helpers, eg Html.TextBoxFor(model = > model.Name). I've used Editor templates for nested objects. (My backend is a collection of Mongo documents, so I need to represent complex types).
Then I need a dropdown. It turns out dropdowns are a bit finicky; no problem, I'll make a viewmodel instead of passing in the item directly:
class itemViewModel
{
...
public Item item { get; set; }
public IEnumerable<SelectListItem> dropdown { get; set; }
}
public ActionResult()
{
return View("Update", new itemViewModel(item, dropdown))
}
... that works fine, the dropdown populates. But! my view requires updating:
Html.TextBoxFor(model => model.Name) ->
Html.TextBoxFor(model => model.item.Name)
Great, problem solved. Oops, now my model binding doesn't work. I debug and look at the Request.Form values: Oh. item.Name instead of Name. Makes sense. I tell my Update view to expect an itemViewModel instead, and the binding works.
Oh wait, no it doesn't. Because I have nested objects that use editors. They are strongly typed and they don't know that the model they're receiving is actually a property of the viewmodel. So they're still spitting out Address.City instead of item.Address.City, and the binding fails.
I can think of several workarounds:
Write specialized custom model binder
Put the whole damn form into its own typed editor, so it gets the item model without knowing it's a property
Kill the viewmodel and hack the dropdown using the ViewData dictionary
Quit using the HtmlHelpers and hand-write the whole form
Write my own HtmlHelper extensions that will take a lamba and a model object as parameters.
Put every label/field grouping into an individual editor template.
All of these feel like either overkill or sloppiness. Viewmodels seem to be a clean, helpful approach. Does using them mean that I have to be sloppy in other areas, or reproduce minor variations on sizeable chunks of the framework? I taught myself C# over the last three months (a graphic designer trying to figure out what the hell static typing is with no CS background was probably pretty funny to watch). I work in isolation; there's no one to learn best practices from. I feel like if I don't learn some of them, I'll end up with an unmaintainable dung heap. So, your opinions are appreciated.
Sigh. A couple more hours of Googling and a few shots in the dark, and it appears that there is an unbelievably straightforward way for doing this, using the Bind attribute:
[HttpPost]
public ActionResult([Bind(Prefix="item")] item)
{
//item's complex types populate correctly
}
The attribute seems to be smart enough to reach into the complex types.
I will leave this as a tribute to my own ignorance, and in hopes that some other hapless n00b will find an answer quicker than I did.
Daniel, first off I should say that I commend you on your efforts and taking on .NET , C# and ASP.NET MVC all in one big bite. Ok, so you're frustrated and I relate to that. It's happens to all of us every now and then.
I should let you know that I'm not a fan (not in the least bit, in fact) of ASP.NET MVC (Problems with ASP.NET MVC Framework Design) and so I can't give you a worked out solution to your problem. But here is how I'd like you to see the situation you're in:
You're in a maze and you've made one wrong turn somewhere and you're going deeper into the maze but you're not going to find a way out. So what you need to do is back out till that wrong turn and see if there is another route. So starting from where you are, ask yourself, "why" for every step/change you've made and back up one step at a time asking why. does that make sense? you'll eventually get to a point where you have other alternative ways to tackle the same (original) problem.
All my controllers are based off of a BaseController, to share properties between them and override OnActionExecuting to set some values based on the route.
I'm creating a BaseViewData class to do the same for all my view data.
At the moment I'm populating the view data like so (C#):
var viewData = new BaseViewData
{
Name = "someName",
Language = "aLanguage",
Category = "aCategoryName"
};
I do this in every action that requires the view data. Some of the properties are common, need to be set throughout every action. Is there a way to set some of the properties on a more global scale?
If I instantiate the BaseViewData class in the OnActionExecuting method on the BaseController, how do I access the BaseViewData properties from the action in the regular controllers (derived from the BaseController)?
Update in response to Dennis Palmer:
I'm essentially doing this because of a nagging issue I'm having with ViewData["lang"] not being populated randomly on some requests. ViewData["lang"] contains "en" if the language is English, and "ja" if it is Japanese (well, it's supposed to anyway). I populate ViewData["lang"] inside OnActionExecuting on the BaseController.
In my view, I make a call to some partial views based on the language:
<% Html.RenderPartial(ViewData["lang"] + "/SiteMenu"); %>
But I'm randomly getting errors thrown that state "Cannot find /SiteMenu", which points to the fact that ViewData["lang"] has no value. I just cannot find any reason why ViewData["lang"] would not get populated. So, I'm rewriting the site to use ONLY strongly typed view data (and setting some hard defaults). But if another method is better, I'll go that way.
Thank you!
I'm not sure I follow exactly what you're trying to do, but if your view is using values in the route to display certain information, it seems like adding your own extension methods for HtmlHelper would be a better way to go.
Are Name, Language and Category contained in your routes? If so, then HtmlHelper will have access to the route info and can determine what to display via the extension methods. What is the correlation between your routes and what your views need to know?
Update: Is lang part of your route? If so, then I would still contend that you could write an HtmlHelper extension method that looks at the route data directly and determines which partial view to render. That way your controller wouldn't even need to worry about setting the ViewData["lang"]. The view would always know how to render based on the route.
Update 2: I think dismissing use of an HtmlHelper extension method because it re-evaluates the route data might be a case of premature optimization. Your controller inheritance scheme sounds overly complex and you asked the question because the way you were setting ViewData was unreliable. I doubt that pulling the value from route data would be much, if any, less efficient than setting and reading from ViewData.
From your comment:
In the controller I use the lang value
to determine which view to show as
well.
That only makes me think that there are more pieces of your system that I'd need to see in order to give better advice. If you have separate views for each language then why does the view need to be told which language to use?
Another alternative to consider would be using nested master pages. You could have a single master page for your site layout and then a nested master page for each language that just contains a hard coded lang value.
Perhaps instead of this inheritance scheme you have, you can just use action filters to add the data you need.
I'm writing a view helper based on the ideas about partial requests from this blog post: http://blog.codeville.net/2008/10/14/partial-requests-in-aspnet-mvc/
In the controller action I prepare a widget by running:
AddWidget<Someontroller>(x => x.OtherActionName(new List<int>()));
Then in my view I can run the action and render the view output by doing some form of:
Html.RenderWidget...
And here comes my question, what compiler checked syntax would you choose from the following to use in the view:
Html.RenderWidget<SomeController, List<int>>(x => x.OtherActionName);
Html.RenderWidget<SomeController>(x => x.OtherActionName(null));
Html.RenderWidget<SomeController>(x => x.OtherActionName(It.IsAny<List<int>>);
Can anyone name some pros and cons? Or is it better to go with strings as the original Partial Request implementation does?
PS. Don't take the naming of It.IsAny> to litteraly, I just thought it was best described using the Moq naming.
Using the strings is significantly faster than using so-called strongly typed helpers (really; it's like 10 times faster), unless you implement some kind of caching for your Expression parsing. Note, though, that MVC 2 may have something along these lines RSN. So one option, if you can, is to just wait and see what's in the next preview drop. At the very least, you'll want to look like the rest of MVC, and the MVC team may end up doing your work for you.
("So called" because under the covers they're going to end up as strings in a RouteValueDictionary anyway.)