Assuming a web page, where we want to display a list of movies, register movies and edit those movies.
You could create a controller, and within the controller treat the three views (Index, Create, Edit).
Or you could create two controllers, one for treating the Index and the other to address the Create and Edit. (Since Creating and Editing Model or ViewModel share)
Or you could create three controllers, one for each view.
It can be done in three ways.
But which one is right and why?
you'd want to create one controller for this, MoviesController, for example. The operations within the controller will then make sense since the URL will look like this...
Movies/Create
Movies/Edit
Movies //this will just list the movies.
The controller could then be passed a Movies repository for manipulating or listing the data.
Obviously, you can create three controllers, but this would be overkill and cause maintenance headache.
Because your business model is one , i think you have to use one controller and your required actions and views.
Controllers should be grouped logically around an object\functionality. If all of these three actions are related to Movies, then it should be MovieController with three actions\views.
Making new controller for each action will make your code a bit of mess.
You should create a controller with three actions and views (Index, Create, Edit).
Related
I've become a little lost trying to figure out the correct way to share some data between some partials. I'll do my best to explain the setup:
Layouts
_Layout.cshtml - this is the main layout for the site: top navigation, footer etc.
_SectionLayout.cshtml - this is a sub-layout of _Layout.cshtml and makes the main content area of the site into a header and two lower columns - the left column is a menu, the right loads various page content
Partials
_SectionMenu.cshtml - this is the mark-up for the left hand column menu, it builds the menu based on an ViewModel that it expects to be passed which contains an enumerable list of menu items
_SectionHeader.cshtml - this is the mark-up for the section header, it builds the header similarly to the menu above
All of the above are located in the Shared folder.
A series of controllers use the _SectionLayout style to have a consistent style for areas of the site with related pages. The layout is specified on these controllers via a _ViewStart.cshtml within the Views folder for each controller. Each controller implements an ActionResult method for both _SectionMenu() and _SectionHeader(), returning appropriately populated ViewModels for the section.
The individual pages that the menu loads have been setup to do something along the lines of the following:
if (Request.IsAjaxRequest())
return PartialView();
else
return View();
So that they can be loaded either directly via a standard GET request (which loads the header, menu and layout), or as a partial to be inserted into the right hand column of the layout. Now this is all seems to work well, until I come to deciding how to load data that is shared between actions and partials within the same controller while trying to adhere to DRY where I can and cut down on code re-use. The problem is as follows:
Within a typical controller of this type, a single full load of the page will invoke three ActionResult methods: _SectionHeader(), _SectionMenu() and whatever the main action was. Often, all three of them need to access data from the same, or similar database queries. For example, a ProductController may have an Index action which shows an overview of the product information. The header will need to show the name of the product, the menu will show statistics such as the number of images. All of this data comes from a single query which returns all of the product information from the database, but how am I best able to share this data between the three actions? Some ideas I've had:
Make all the ViewModels inherit from a base view model for that controller and pass it through to the partials after opening it in the main action
Store the data in the ViewBag or ViewData
Just open the database multiple times and hope that my ORM is clever enough to realise it doesn't need to fetch it from the server multiple times within the same request
The first one seems to me to be rather complicated for what is a fairly simple ask. The second always feels like a cheap easy way out and I try to avoid it. The third, I'm not confident enough that what I'm using (Dapper) will know not to go to the server again for the same query.
To make things just a little more complex, some of the Actions for a controller may not need to load the main database query. To extend the example from above, the Images option in the menu of the Product controller may need to run an additional query to load the list of associated images, but if it is being requested as an ajax call, it really doesn't need to load the main query for the header and menu sections.
I'm comfortable with the logic for that, but does every one of the actions within a controller of this type need to look something like the following, or can DRY be leveraged here?
public ActionResult Images(int? id)
{
if (!CheckExists(id))
return HttpNotFound();
// if this is an action that doesn't normally need the shared data,
// only load it if it isn't an ajax request, as then the menu/header will require it
if (!Request.IsAjaxRequest())
LoadSharedData(); // part of the question is how I handle this part
// load the particular model for this page
var model = GetImagesModel();
if (Request.IsAjaxRequest())
return PartialView(model);
else
return View(model);
}
In a section with say 10 similar actions in it, seeing all that extremely similar code repeated tells me that I'm doing something wrong. Thanks in advance for any help. If I haven't explained something well, please ask and I'll do my best to clarify.
Hi I have spent hours trying to work out how to do this one.
So lets say I have two models:
model 1 - AccountInformation
model 2 - AccountHoldersImages
One account holder can have many images. I am trying to achieve on the accountInformation create view (using Scaffolding) to include the facility to add the images at the same time of creation of the user account. Is this possible?
One account holder can have many images
In which case, AccountHoldersImages must be a part of AccountInformation.
If in different views you are using AccountHoldersImages separately, then may be you can keep the view models separate.
However, for this particular view that you are talking about, you will have to wrap the view models into a single view model as MelancialUK commented above.
Another way is to pass the models as part of ViewData[]. If it is a form, then on post you can use FormCollection parameter of your [HttpPost] action to get your images.
How to send more than one model to the View from Controller?
This seems to be a question that is asked so many times, still there is no good answer for newbyes like me (I have not found it).
One sollution I have found is to create some "Parent" model and return collection of Parent child models. I do not want to create any parent model as both my models are not related to each other.
For example, I have two models that do not have relations between them, they are seperate models, for example, PersonModel and HardwareModel. I have two partials views, one needs PersonModel, another needs HardwareModel.
I have HomeController that returns View. This view displays both partial views. So I need to send PersonModel to _PersonPartialView. And I need to send HardwareModel to _HardwarePartialView.
How to do this?
I believe there should be an option to send Collection of unrelated models to View, but how exactly?
Edit:
Some explantions: we have complicated decisions, based on those we show one ore both partial views. You can think like dashboard. User can see one ore more "dashobard" like panels. So they could be even unrelated to each other. So the real situation is more complicated as we have more than 2 different models and different partialviews.
Maybe I should have absolutely different approach.
If the HomeView requires both PersonModel and HardwareModel, then those two combined are your model. So create eg
class HomeModel
{
PersonModel person;
HardwareModel hardware;
}
and you have your model.
Update
Based on the question update, if you have a dashboard-like page, then one option is to do away with the main view as you currently have it. Have a skeleton view, which defines the panel locations, but not their content. Then use AJAX calls to request partial views to populate the panels. That way, each partial view has its own model, separate from the others and you avoid having one view that need to know about all the models for all the partial views it might end up hosting.
We ended with putting abstract things that are part of several views, but not necessarily part of each model (e.g., cultures) into the ViewData / ViewBag. You can still access them in a strong-typed way by providing an extension method that encapsulates the view bag through a additional class. I'd suggest to put HardwareModel stuff into these, because it sounds like it's not the main thing on your web page.
public static HardwareSettings GetHardwareSettings (this HtmlHelper html)
{
// simplified; add lazy instantiation...
return (HardwareSettings) html.ViewData["hardware"];
}
This is for sure the best you can do. If this doesn't fit your problem, I suggest to reconsider your architecture, as it might be case that there are some flaws in it.
If this models is not linked, you should not return them from single controller method. If it should be showed on one page, you can load this partial views with ajax actions
one of examples how to do this click
This is more of a high-level question than anything else.
I have a MVC project which, among other things, manages users.
I am currently sticking to a more strict approach that on my view pages I only use a model which is declared, meaning that I have a:
#model MVCApp.Models.SomeModel
At the top of each View.cshtml
There are some pages that require more than 1 model. At this point I consolidate the 2 models into 1, and send it to the view as one model.
Here comes the tricky part. Let's say I have some model which holds the user data. That user data is stored in the session cookies (typical Forms Authentication). It seems weird to me that I now have to wrap each and every model I use with my own model that holds both the User Model and the model which I want to use inside that View.
The question that I ask myself is why not pass the User Model to the ViewBag and use it inside the View. Why is that considered to be bad practice? It allows me to attach that model to every page without having to ultimately duplicate all my models.
I'd love to get some guidance. I might be looking at this the wrong way. Any help will be much obliged.
Thanks,
There are a couple of reasons why ViewBag should be avoided:
ViewBag is weakly typed
You don't get Intellisense in the view
Your view is a mixture of ViewBag and a view model and your view gets information from different places instead of centralizing everything into a strongly typed view model
ViewBag doesn't work with strongly typed helpers and expressions because dynamic types cannot be used with extension methods (that's not a limitation of ASP.NET MVC, it's .NET => you cannot dispatch extension methods on dynamic types)
Due to this weak typing nature of ViewBag you will have to cast in your views turning them into spaghetti code because HTML helpers expect specific types to be passed as arguments
... this list goes on (I really don't have time to fill it but it is very large list)
So now that we know that ViewBag is bad and shouldn't be used there are different ways you could solve this requirement by using view models.
One possibility is to use the Html.Action helper which allows you to insert some portion of HTML in the view by going through a full Controller/Model/View lifecycle. This way you could have a commonly used widget without interfering with your main view model.
Another possibility is to have a base view model which will contain a property representing user details and which will be populated by a custom global action filter that could executed everytime a controller action has finished executing and returned a view result. The action filter could intercept this view result, read the authentication cookie information and set the view model property. This assumes that all your view models derive from the common base view model. This obviously makes sense if you need to display this user information on each page.
For example:
public class UserInfoInjectorAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var result = filterContext.Result as ViewResultBase;
if (result == null)
{
// the controller action didn't return any view result => no need to continue
return;
}
var model = result.Model as BaseViewModel;
if (model == null)
{
// the controller action didn't pass a model or the model passed to the view
// doesn't derive from the common base view model that will contain
// the user info property => no need to continbue any further
return;
}
model.UserInfo = ... go ahead and read the forms authentication cookie
userData portion and extract the information
you are looking for
}
}
Now all that's left is to register this action filter as a global action filter and it will be applied on all controller actions.
Now if all your view models derive from this BaseViewModel you will know that once you arrive in the view the UserInfo property will be populated with the relevant information without polluting all your controller actions with code that does the fetching of this property. And still you get strong typing in the view => no ViewBag (youpeeee).
Of course depending on your specific scenario there might be other ways to achieve that (obviously not involving any ViewBag).
You can an ActionFilterAttribute and in those Actions that load views which needs specific ViewBag items, you initialize them.
I don't recommend it, as it will be harder to mantains, but will be a generic approach which may solve your problems.
Is there any way of mapping multiple controllers to a single view directory? In my application I would like to have a 1 to 1 mapping between controllers and views. The reason for this is that each view will have a variety of ajax actions that the controller will provide methods for and having multiple views per controller will result in overly large controllers.
Presently I have something like this:
/Controllers
/Dir1Controller
...
/Views
/Dir1
/FirstView.cshtml
/SecondView.cshtml
...
So Dir1 Controller maps to the directory Dir1 and needs to provide all the actions for the views FirstView, SecondView, etc. Instead, I would prefer something like this:
/Controllers
/FirstController
/SecondController
...
Where both controllers map to Dir1 but return the appropriate view when an action is executed.
None of this seems to be a problem on the route mapping side of things but there doesn't seem to be a way for me to tell it how to find the correct view. FirstController will only look in /Views/First for views. Is there any way I can direct it to look in /Views/Dir1?
I looked at using areas but they seem to be directed at spiting up large sites. Conceptually all of these pages should be part of the same area. I just want to keep the size of my controllers bounded.
If your Ajax calls are related to a single controller then stick to the existing pattern and have methods in your controller. If they can be reused put them in partial views or controllers organized by their purpose and when doing your Ajax calls simply request the controller and action. This is the typical pattern for a reason, why step outside of it? :)
Why not just return the view explicitly?
return View("~/Controllers/FirstController/view.cshtml");
If all the actions in a controller return the same view, use a constant at the top of the Controller class, something like:
public class myController
{
private string ViewPath = "~/Controllers/FirstController/view.cshtml";
...
That way you only have to set the path to the view once in each controller. Then in each of your action methods:
return View(ViewPath);