MVC ViewBag Best Practice [closed] - asp.net-mvc

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
Improve this question
For the ViewBag, I heard it was a no-no to use.
I would assume have the content from the ViewBag should be incorporated into a view model?
Question:
Is my assumption above the best practice. (Not to use a ViewBag and second to have it in the view model)
Are there situations where a ViewBag is absolutely necessary?

ViewBag is a dynamic dictionary. So when using ViewBag to transfer data between action methods and views, your compiler won't be able to catch if you make a typo in your code when trying to access the ViewBag item in your view. Your view will crash at run time :(
Generally it is a good idea to use a view model to transfer data between your action methods and views. view model is a simple POCO class which has properties specific to the view. So if you want to pass some additional data to view, Add a new property to your view model and use that.Strongly typed Views make the code cleaner and more easy to maintain. With this approach, you don't need to do explicit casting of your viewbag dictionary item to some types back and forth which you have to do with view bag.
public class ProductsForCategoryVm
{
public string CategoryName { set;get; }
public List<ProductVm> Products { set;get;}
}
public class ProductVm
{
public int Id {set;get;}
public string Name { set;get;}
}
And in your action method, create an object of this view model, load the properties and send that to the view.
public ActionResult Category(int id)
{
var vm= new ProductsForCategoryVm();
vm.CategoryName = "Books";
vm.Products= new List<ProductVm> {
new ProductVm { Id=1, Name="The Pragmatic Programmer" },
new ProductVm { Id=2, Name="Clean Code" }
}
return View(vm);
}
And your view, which is strongly typed to the view model,
#model ProductsForCategoryVm
<h2>#Model.CategoryName</h2>
#foreach(var item in Model.Products)
{
<p>#item.Name</p>
}
Dropdown data ?
A lot of tutorials/books has code samples which uses ViewBag for dropdown data. I personally still feel that ViewBag's should not be used for this. It should be a property of type List<SelectListItem> in your view model to pass the dropdown data. Here is a post with example code on how to do that.
Are there situations where a ViewBag is absolutely necessary?
There are some valid use cases where you can(not necessary) use ViewBag to send data. For example, you want to display something on your Layout page, you can use ViewBag for that. Another example is ViewBag.Title (for the page title) present in the default MVC template.
public ActionResult Create()
{
ViewBag.AnnouncementForEditors="Be careful";
return View();
}
And in the layout, you can read the ViewBag.AnnouncementForEditors
<body>
<h1>#ViewBag.AnnouncementForEditors</h1>
<div class="container body-content">
#RenderBody()
</div>
</body>

1) Is my assumption above the best practice. (Not to use a ViewBag and
second to have it in the view model)
You should use viewmodels instead of passing data via ViewBag as much as possible.
2) Are there situations where a ViewBag is absolutely necessary?
There is no situation where a ViewBag is absolutely necessary. However, there are some data I personally prefer using ViewBag instead of View Model. For example, when I need to populate a dropdown box for predefined values (i.e Cities), I use ViewBag for carrying SelectListItem array to view. I prefer not to pollute my ViewModels with this data.

1) Is my assumption above the best practice. (Not to use a ViewBag and
second to have it in the view model)
Yes.
2) Are there situations where a ViewBag is absolutely necessary?
No. Everything that you have stored in a ViewBag could go into the view model passed to the view.

The issue with ViewBags and the recommended best practice boils down to compile time checking. ViewBags are just dictionaries and with that you get 'magic' strings, so if you end up changing the object type of one of the viewbag items or the key name you won't know until runtime, even if you precompile the views using <MvcBuildViews>true</MvcBuildViews>.
Sticking to view models is best, even if you have to alter them to fit a specific view now and again.

I have found some use for ViewBag where there is common functionality across all pages, and the functionality does not depend on the page being shown. For example, say you are building StackOverflow. The job board appears on each page, but the jobs shown have nothing to do with the page (my usage was similar in concept). Adding a property to each ViewModel would be difficult and time consuming, and a lot of fluff to your tests. I don't think it is worth it in this situation.
I have used a base ViewModel class with the cross cutting data, but if you more than one (e.g., jobs & list of stack exchange sites), you either have to start stuffing extra data in, or some other abuse of a ViewModel, plus you need a ViewModel builder to populate the base data.
As for the magic strings problem, there are a lot of solutions. Constants, extension methods, etc.
With all that said, if you have something that is displayed on your page that depends on the context of the page, a ViewModel is your friend.
Erick

No. Use ViewModels.
No. If you design a perfect ViewModel, you never need a ViewBag.

If you cannot re-design EXISTING ViewModel use ViewBag.

If there were no use cases for it, it wouldn't be implemented in the first place. Yes you can do everything with ViewModels, but what if you don't really need one? One such scenario is editing entities. You can pass DTO directly as a model.
#model CategoryDto
<div class="md-form form-sm">
<input asp-for="Name" class="form-control">
<label asp-for="Name">("Category Name")</label>
</div>
But what if you want to select Category parent? Entity DTO ideally holds only it's own values, so to populate select list you use ViewBag
<select asp-for="ParentId" asp-items="ViewBag.ParentList">
<option value="">None</option>
</select>
Why do this? Well if you have 50 types of entities each with some sort of select from different values, you just avoided creating 50 extra ViewModels.

I thought I'd give my opinion on this, as I've used the ViewBag extensively.
The only real benefits you'll get from using it are for a small project, or in cases where you have a new project and just want to get the ball rolling and not have to worry about creating loads of model classes.
When your project is more mature, you may find issues with unexpected behaviour caused by using weak typing all over your application. The logic can be confusing, difficult to test and difficult to troubleshoot when things go wrong.
I actually went down the route of completely removing the ViewBag from my applications, and preventing other developers from using it within the same codebase by throwing compilation errors when they try to, even within Razor views.
Here's a sample ASP.NET 5 project on GitHub where I've removed it: https://github.com/davidomid/Mvc5NoViewBag
And here's a blog post where I explain the motivations for removing it and an explanation on how the solution works:
https://www.davidomid.com/hate-the-aspnet-mvc-viewbag-as-much-as-i-do-heres-how-to-remove-it

2.Are there situations where a ViewBag is absolutely necessary?
In some case you'll need to share your data from the Controller across the layouts, views and partial views. In this case, ViewBag is very helpful and I doubt there's any better way.

At the risk of opening up an old can of worms here, let me offer a tiny bit of insight. Regardless of what might be the "best practice," let me offer the real-world experience that the extensive use of ViewBag can be a nightmare source of niggling, hard to find bugs and issues that are an utter nuisance to track down and resolve. Even if an agreeable notion or rule for the use of ViewBag can be established, they too easily become a crutch for junior developers to rely on to distraction AND discourage the development of proper, strongly typed ViewModels.
Unfortunately, too many "tutorial" YouTube videos show the use of ViewBags for a demonstration purposes and offer little to no insight on when such a practice isn't appropriate for production code. Yes, there may be times when use of the ViewBag may be a suitable solution to a given problem, but can lead to a long and winding road of frustration and poor maintainability. At the risk of overcorrecting in the cautious direction, I would encourage younger developers not to rely on the ViewBag until they get more experience with MVC, develop a natural sense of when and how ViewModels are useful, and then after that time develop a more seasoned sense of when the use of the ViewBag is appropriate.

Related

ASP.NET MVC: Is "views must be dumb" correct statement?

Most of the good references for MVC on web strongly suggest "views must be dumb". It should not contain any logic. This does not seems valid when I try to implement it.
Some of my views display only first name of student and some of them display only last name and some of them display full name. My database (and POCO and DTO) store name in separate components. I see best place to format the name is view.
My view changes on some actions on client side without hitting server. Example, on clicking some button, it hides some part of view and shows other and disable some controls. Or another button opens new window and takes some inputs and validates it. This input is never sent to server; it is only useful for some client side activities.
I have validations on server side. But just to save the hit to server, I also validate on client side.
Data binding with KnockoutJS.
Based on data (start and end date) received from server, I generate table on client side to display sections of that period slicing 1 hour each. This is something scheduling like GUI. I need to do date and time calculations in view to achieve this. When I was in stone age (ASP.NET WebForms), I was generating this table on server side; I know you are shocked. I shifted it to JS for performance.
In SPA, view hold most of the logic by fetching only necessary data from server through AJAX.
I can put many other similar examples here those force me to put some logic in view. Considering that view still holds logic and use of JS is increasing day by day, can we still say "views must be dumb" is a correct statement?
Some details explaining the same with respect to points above will help.
Notes:
Though my question is based on ASP.NET, I am expecting answers referencing to ONLY MVC as a design pattern; no matter what technology I use. So please do not suggest what is another way to do validations. Points above are just to note some of the cases where logic is necessary in view.
There might be something wrong in how I am implementing above points. But My only point is that, use of JS (and putting logic in views as a result) is increasing.
Everyone is welcome to contradict above points in case I am implementing it wrong way; just do not redirect entire discussion that way.
Edit 1:
#kayess: Yes, I have Models as well as ViewModels and this is obvious in MVC. Server logic that is strongly related to specific view and cannot be reused is put in ViewModels. Major part of domain logic which is reusable is put in Models. Even after having ViewModels, there are many things those I will prefer to do on client side. About narrowing the question, the basic answer to this question will be "Yes" or "No". Other details will be just to support the answer. I do not think this will attract opinions as there must be something about MVC views that I have not fully understood. The one answering the question just need to point that out to me.
I think generally by "views must be dumb" means specifically the server side part of the views. Having TypeScript/JS in a view is perfectly normal. I wouldn't however expect to have dozens of lines of C# in the view that is fetching records from a database.
However, having some very simple logic such as the following is pretty common:
#{
if(user.IsLoggedIn)
{
<span>You have new messages!</span>
}
else
{
<span>You need to login to view messages.</span>
}
}
However server side view code shouldn't get much more complicated than that because that breaks down the whole point of separation concerns and having appropriate abstractions and design patterns etc, and just becomes unmaintainable.
See also: Microsoft documentation on "Adding a View"
May it be any technology there are set of theories supporting different concepts, like you said view should be dumb there are advocates of model should be dumb.
Idea here is let your view model take care of the manipulation if any needed, and let your view refer to view model. So that change is centric.
And I believe you are alredy doing that.
Although this is an old question and has an accepted answer, I want to add some points not covered in any answers.
I think you are overlooking cases where more logic can be pushed to your domain model. For example, you mention the concept of "Name" and that you must use conditional logic in your views to decide whether to show first, last or full name. I have a class called Name in my domain model. It incorporates the logic of FirstName, LastName and FullName like so:
public class Name
{
public Name(string firstName, string lastName)
{
this.FirstName = firstName;
this.LastName = lastName;
}
public string FirstName { get; }
public string LastName { get; }
public string FullName
{
get { return $"{this.FirstName} {this.LastName}"; }
}
}
In my UI I have have ViewModels with the whatever name property I need. When I convert my domain models to view models my conversion logic tells the ViewModel which property to use. If I need FullName I use Name.FullName. If I just need FirstName, I use Name.Firstname. At no point do I need to put conditional logic about names in my views.
In addition, you can use custom helpers, formatters and extensions to encapsulate reusable logic for views. For example, I have utilities to format phone numbers, addresses etc, so that I do not have to put this logic in every view that needs it. I also have a paging module in my UI that encapsulates paging so that I do not have to have paging logic in my views other than to call the pager.
Partial views are also helpful. For example, from the accepted answer, I would put the following in a partial view and just call it in each view, rather than repeating the conditional logic it in every view:
#{
if(user.IsLoggedIn)
{
<span>You have new messages!</span>
}
else
{
<span>You need to login to view messages.</span>
}
}
My point is there is a lot you can do to move logic out of views to keep them dumb.

Purpose of viewmodel in controllers when data already present

What is the point in using a viewmodel when all the relevant data is already available through the parameter in the signature of the controller? I've seen a lot of examples similar to this:
public ActionResult Index(BasicPage currentPage)
{
var model = new BasicViewModel { Heading = currentPage.Heading, Body = currentPage.MainBody, MyBlock = currentPage.MyBlock };
return View(model);
}
Why not just send in the "currentpage" in this example directly to the View? Is there something that is considered bad practise by doing like that?
The above was a general question for asp.net-mvc. I'll add a question about Episerver as well here as well and if I'm lucky someone can answer that as well.
When looking through the Alloy site done with MVC I also saw similar behavior like above where a viewmodel is created in all controllers, why not just send in the Page that is sent into the controller directly to the View? It seems like an unnecessary step to create the viewmodel? Most likely I'm missing some important point here =)
The idea behind viewmodels is that they are optimised for presentation. If you use domain objects you end up having to put logic into the view itself to control the way things are displayed. Putting logic into the view is poor design so the viewmodel gives you some flex so that you have this logic managed in a more appropriate place.
In addition, you can make your viewmodel simpler, only including the fields that are required for the specific view.
Your code example is not a good idea I agree. But having a view model is a good pattern. You might not need it right away but having a view model available and in place is a good option to have for upcoming additions. I would go with Joel's concept from his MVC templates: http://world.episerver.com/Download/Items/EPiServer-CMS/EPiServer-7---CMS/EPiServer-7-MVC-Templates/

How to update Model when binding to a ViewModel?

I have an [HttpPost] action method signature like this:
[HttpPost]
public ActionResult Edit(ExistingPostViewModel model)
{
// Save the edited Post.
}
Now, in the past (when i didn't use ViewModels, e.g R&D), i had an implementation of an Edit method like this:
[HttpPost]
public ActionResult Edit(Post model)
{
var existingPost = repo.Find(model.Id);
TryUpdateModel(existingPost);
repo.Save(existingPost);
return RedirectToAction("Success", existingPost.Id);
}
Which worked great.
But i'm confused how to adapt the above to the ViewModel approach.
If i do this:
TryUpdateModel(existingPost)
With my ViewModel approach, not much happens. No errors, but nothing is being updated because MVC won't know how to update a Post from a ExistingPostViewModel (before it was Post -> Post).
Now, i'm using AutoMapper. So i thought i could map from the ViewModel to the Post, then save the post.
But then im basically overriding everything. Which i don't want to do and defeats the point of the cut down ViewModel.
Can anyone un-confuse me?
This seems like a really common scenario, and i am totally stumped as to how people solve this. I can only see 3 possible solutions:
Don't use a ViewModel in the HTTP POST. As i said i did this in the past for R&D and it works, but now i see how my View's have evolved (validation, simplicity), and i can't compromise that just for the sake of this problem.
Don't use TryUpdateModel. Possibly, but then how would i merge in the changes?
Use left-to-right. Ugh. But at the moment this seems to be the way im leaning.
Someone please give me solution #4! :)
BTW, i'm using ASP.NET MVC 3, Razor and Entity Framework.
I actually ran into this exact same issue in a project I'm currently working on. As much as I wasn't a fan of it, I ended up doing the left to right approach and manually mapping my viewmodel data back to my entity.
The only nice thing about this approach is it does give you more control. Since I started using more compound viewmodels, where you actually have fields from more than one entity in your viewmodel, it started making more sense to do things this way.
I'm also using AutoMapper, and you're absolutely right, it does get awkward when you're trying to do a simple update operation. Wish I had some super clever workaround for you, but the "old fashioned way" seems to get the job done best for the work I've been doing.
Not sure if this will help, but it is working for me. I have my underlying domain table as a Visitor Object. My viewmodel contains the Visitor Object plus a couple of IEnumerables for dropdowns.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id)
{
Visitor Visitor = new Visitor();
Visitor = db.Visitors.FirstOrDefault(v => v.VisitorID == id);
UpdateModel(Visitor, "Visitor");
db.SaveChanges();
return RedirectToAction("Index");
}
The UpdateModel works off my viewmodel because of the "Visitor" string telling it what values to compare.
For simple things where you don't have to run any controls prior to implementing the update what you are doing is okay (db.get(), and then update).
When things get complicated, you have to load the entity, and then select and apply user's changes from the view model, property by property. In those cases, you end up writing Update methods, which gets the new data as input, and then you load the existing entity, then compare states, and take the required actions based on the view model data. Actually in this case, probably you wont have an Update method but will have behaviors, like CancelPost, AddComment, EditPost (which also logs the edit reason), AddTagsToPost etc.

ASP.NET MVC: having one model for all views

I like the way model binding in django works: you send stuff that you need in a Json-like way:
'param1': val,
'param2': val
In ASP.NET MVC you can't do it primarily because C# is a static language. You can use ViewData but it's ugly and not recommended.
So I had to create dozens of ViewModels for every view: IndexViewModel, AboutViewModel, etc. And today I got an idea: why not just create one Model class and have Visual Studio generate all necessary fields? It's almost like in django.
return View(new Model
{
Param1 = "asd",
Param2 = "sdf"
});
Param1 & Param2 are not members of the Model class, but Visual Studio will automatically generate them.
Now my question is, is it gonna work? The thing is that there will be a lot of fields in this Model class. And when we pass it to a view, only a small percentage of fields made for that view in particular will be used. Is it gonna be bad performance wise?
Thanks
If you have all these properties on your ViewModel which aren't used (i.e null or whatever) it isn't really going to impact performance. What it will impact however, is your design.
Generally speaking, one model to rule them all as you are proposing is a bit evil and goes against separation of concerns. ViewModel's should be very simple and should be tailored to the view. ViewModel's shouldn't really provide the view with more or less data than what the view needs in order to render.
Consider this....
You have a generic model with 15 properties on it and you only set a handful of them. Somebody else designs a new view and looks at the model, they may not know which of those properties are sent and under what conditions they are set. Consequently they may be attempting to display data that does not exist. This isn't a very clean approach.
I would stick to individual view models and where there is common functionality between views, create an abstraction or base ViewModel from which other view models can extend.
Edit: One other thing you could do is use the new MVC 3 (still in preview) syntax (dynamic) for setting ViewData properties directly as if they were properties.
So rather than doing
ViewData["FirstName"] = "Bob";
You can do
ViewModel.FirstName = "Bob";
This gives you dynamic variables automatically in MVC 3.
Have a look a Clay the 'malleable' object model being used by the Orchard Project.
http://weblogs.asp.net/bleroy/archive/2010/08/16/clay-malleable-c-dynamic-objects-part-1-why-we-need-it.aspx
http://weblogs.asp.net/bleroy/archive/2010/08/18/clay-malleable-c-dynamic-objects-part-2.aspx
There's no reason it shouldn't work.
You can even skip the Model class and create an anonymous type with just the members you need.
return View(new { Amount = 108, Message = "Hello" });
The problem using anonymous types is that you're giving up on auto-completion in the view since you won't be able to type the view according to the model.
Just use dynamic

ModelFactory in ASP.NET MVC to solve 'RenderPartial' issue

The 'RenderPartial()' method in ASP.NET MVC offeres a very low level of functionality. It does not provide, nor attempt to provide a true 'sub-controller' model *.
I have an increasing number of controls being rendered via 'RenderPartial()'. They fall into 3 main categories :
1) Controls that are direct
descendants of a specific page that
use that page's model
2) Controls that are direct
descendants of a specific page that
use that page's model with an
additional key of some type.
Think implementation of
'DataRepeater'.
3) Controls that represent unrelated
functionality to the page they appear
on. This could be anything from a
banner rotator, to a feedback form,
store locator, mailing list signup.
The key point being it doesn't care
what page it is put on.
Because of the way the ViewData model works there only exists one model object per request - thats to say anything the subcontrols need must be present in the page model.
Ultimately the MVC team will hopefully come out with a true 'subcontroller' model, but until then I'm just adding anything to the main page model that the child controls also need.
In the case of (3) above this means my model for 'ProductModel' may have to contain a field for 'MailingListSignup' model. Obviously that is not ideal, but i've accepted this at the best compromise with the current framework - and least likely to 'close any doors' to a future subcontroller model.
The controller should be responsible for getting the data for a model because the model should really just be a dumb data structure that doesn't know where it gets its data from. But I don't want the controller to have to create the model in several different places.
What I have begun doing is creating a factory to create me the model. This factory is called by the controller (the model doesn't know about the factory).
public static class JoinMailingListModelFactory {
public static JoinMailingListModel CreateJoinMailingListModel() {
return new JoinMailingListModel()
{
MailingLists = MailingListCache.GetPartnerMailingLists();
};
}
}
So my actual question is how are other people with this same issue actually creating the models. What is going to be the best approach for future compatibility with new MVC features?
NB: There are issues with RenderAction() that I won't go into here - not least that its only in MVCContrib and not going to be in the RTM version of ASP.NET-MVC. Other issues caused sufficent problems that I elected not to use it. So lets pretend for now that only RenderPartial() exists - or at least that thats what I've decided to use.
Instead of adding things like MailingListSignup as a property of your ProductModel, encapsulate both at the same level in a class like ProductViewModel that looks like:
public class ProductViewModel() {
public ProductModel productModel;
public MailingListSignup signup;
}
Then get your View to be strongly-typed to the ProductViewModel class. You can access the ProductModel by calling Model.productModel, and you can access the signup class using Model.signup.
This is a loose interpretation of Fowler's 'Presentation Model' (http://martinfowler.com/eaaDev/PresentationModel.html), but I've seen it used by some Microsoft devs, such as Rob Conery and Stephen Walther.
One approach I've seen for this scenario is to use an action-filter to populate the data for the partial view - i.e. subclass ActionFilterAttribute. In the OnActionExecuting, add the data into the ViewData. Then you just have to decorate the different actions that use that partial view with the filter.
There's a RenderPartial overload I use that let's you specify a new ViewData and Model:
RenderPartial code
If you look at the previous link of the MVC source code, as well as the following (look for RenderPartialInternal method):
RenderPartialInternal code
you can see that if basically copies the viewdata you pass creating a new Dictionary and sets the Model to be used in the control. So the page can have a Model, but then pass a different Model to the sub-control.
If the sub-controls aren't referred directly from the main View Model, you could do the trick Marc Gravell mentions to add your custom logic.
One method I tried was to use a strongly typed partial view with an interface. In most situations an agregated ViewModel is the better way, but I still want to share this.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IMailingListSignup>" %>
The Viewmodel implements the interface
public class ProductViewModel:IMailingListSignup
Thats not perfect at all but solves some issues: You can still easily map properties from your route to the model. I am not shure if you can have a route parameter map to the properties of MailingListSignup otherwise.
You still have the problem of filling the Model. If its not to late I prefer to do it in OnActionExecuted. I dont see how you can fill a Model in OnActionExecuting.

Resources