I'm currently working on a project in which we have a number of similar models. We made them all inherit from the same base model and we also made a common controller (basic stuff like list, adding, editing etc.) handles the base model. It actually works and made things easy for us, but I have small problem with dropdowns.
Without going into much details, it would make things really easy for me if I could pass data from controller to view using an action. Something like this:
@Html.DropDownListFor(model => model.Xtypechild, Action("GetList", "XController"))
Where action GetList returns IEnumerable<SelectListItem>
This way I could write the code just once and use it for a number of different controllers.
I know it sounds crazy, but if I tried use one of more conventional ways to do this I would end up with redundant code again and would lose some of the advantage we gained from inheriting controllers.
The only alternative I can see right now is to make GetList return JSON with all the items and populate the dropdowns using Ajax/JS. I could also make an action that returns a partial view containing the dropdown, but I'm not sure if that's a good idea.
Thank you far all the input. In the end I went with the following solution:
We had a services class for handling database operations for our models. I just add a method that generates a IEnumerable<SelectListItem> to the base services class. Now I pass the items for drop downs through ViewBag (though it forces me to cast them back to IEnumerable before use in the view). I assign them to ViewBag in the overridden constructor of the inherited controller.
I've best consult the original author of the code if this solution will be ok for our project, though he is on holiday right now. For now this will have to do. On the up side this method produced little code, so it should be easy to replace (if I have to do so).
The code evolved a little bit and now I have something like this in the view (note the int? casting is there because the property was of type int and it wouldn't work otherwise):
Html.DropDownListFor(
model => model.XId,
new SelectList((List<X>)ViewBag.Xs, "Id", "Name", (Model != null ? (int?)Model.XId : null)),
" -- select -- ",
null)
And something like this inside of overridden constructor of the controller:
ViewBag.Xs = db.Xs.ToList();
It's definitelly not the best way to implement dropdowns in razor, but it was the best way to implement them in the project. The reason for this was: there was no time to make the project use ViewModels and override all the actions of base controller to handle them (as well as provide data for the dropdowns in those actions).
The code evolved even further. It's certainly not a good idea to execute queries in the controller constructor if the data will be used just in a handful of views. So now the constructor contains equivalent of:
ViewBag.Xs = (IQueryable<X>)db.Xs;
And the view:
Html.DropDownListFor(
model => model.XId,
new SelectList((IQueryable<X>)ViewBag.Xs, "Id", "Name", (Model != null ? (int?)Model.XId : null)),
" -- select -- ",
null)
This way the query is only executed when a view actually needs the data.