Is it possible to utilize internal methods on controllers to reduce duplication? - asp.net-mvc

in a partial view I have the following:
<%Html.RenderAction(MVC.User.GetComments(Model.UserGroupName)); %>
can I render a Controller's PartialViewResult in a View without going through routing so I can pass arguments directly from the model so that the arguments I'm passing to the controller never get sent to the user or seen by the user?
Currently the method I'm showing at the top throws an exception because no overload is public. I've got it marked as internal so that a user can not access it, only the rendering engine was my intent.

Slap a [ChildActionOnly] attribute on any action method if you want that method to be callable only by RenderAction() rather than the outside world. Or - if you have a whole controller of such methods - slap the attribute on the controller itself.

Related

MVC - How to include a model inside _Layout for Partial View

I've seen how to use Partial Views from within a View and I understand how a model is passed from the View to a Partial View. What I don't grasp is how to include a Partial View inside of _Layout.cshtml so that I can pass it to the Partial View - or, how the Partial View itself can call a Controller Action to create a Model for use.
Specifically, I want to include a Partial View within _Layout.cshtml to display in the header of every page. The Partial View will display a custom setting in the User Profile. I can't think of any way to obtain the Model without making a Controller Action call - but how is this done in/for a Partial View from within _Layout.cshtml?
Is my only option of accessing a Controller Action to build my Partial View's required Model to use a jQuery call? Or is there another way?
The answer is by calling #Html.Action().
It sounds like you're on the right track.
The key is to try to not pass multiple models around, make models complicated, or use the ViewBag needlessly.
Instead, any time you want information on every page, call an action from your _Layout.
For example, you might have a controller called PartialsController or SharedController.
public class PartialsController : Controller
{
[ChildActionOnly]
public ActionResult UserProfilePartial()
{
UserProfileModel model = new UserProfileModel();
return PartialView("_UserProfile", model);
}
}
The ChildActionOnlyAttribute means that users cannot access the action directly. Only your code can call the action. You can also apply the attribute to the controller so that it affects all actions automatically.
Now call the action from your view (_Layout).
#Html.Action("UserProfilePartial", "Partials")

using object available to view, unavailable to partial view

I have a view and a partial view. Within the controller action methods, I have a using statement which establishes some back-end threading context, including providing access to a model object for display in the view.
The problem that I'm having is that while the context and the associated model object are available for the main view, they are disposed before the partial view is rendered. This seems to be the case regardless of whether I pass the model object from the view to the partial view, or reference it some other way.
The controller view action method:
public ActionResult Index()
{
using (var context = GetContext()) {
return View(Context.MyModelObject);
}
}
The controller partial view action method:
[ChildActionOnly]
public ActionResult MyPartialView(ModelObject modelObject)
{
return PartialView(modelObject);
}
The view:
#model ModelObject
#Model.Name
#{Html.RenderAction("MyPartialView", "MyController", new {modelObject = #Model});}
The partial view:
#model ModelObject
#foreach(var item in Model.Items) {
<div>#item.Name</div>
}
If I comment out the RenderAction line of the view, the view displays just fine, accessing the property of the model because the context has not yet been disposed at the end of the using.
However, if I leave it in with the intent of the partial view being rendered, the context has been disposed of, leaving my model object no longer accessible. I would think the using in the main view controller method would not be finished until the view and all its partial views are rendered, but that doesn't seem to be the case. Are partial views deferred to be rendered at some other time? Is there a way around this?
Right now I have a solution which will involve writing a lot of extra code: making a "view model" object which has only the bits of the model object that I need for display in this specific view while it hasn't yet been disposed, then having the view and partial views use that. However, this will be very undesirable as it will result in a lot of duplication between the model and the view models, which will be a subset. It would be very convenient to use the model directly in the view.
The other possibility is that we could alter our backend to dispose of things differently such that they are still available for the partial view whenever it gets around to rendering, but this also seems like a hack.
In summary, is there a better way to do this in the view/controller? Am I making some erroneous assumptions?
MVC was designed with Unit testability in mind. As such, it doesn't render the view when you call View(), it just creates a structure which gets rendered after it returns from the controller.
This design makes it easier to unit test controllers because it doesn't actually require an active web server connection to execute the controller logic.
But, this also means that since you have a using statement in your controller, the view is not actually rendered until after you have returned from the function, and thus the using has called delete on the data context. Thus, your queries will not execute.
You get around this by executing the query within the controller, such as calling .ToList() on the object. You still have to be careful not to execute any lazy operations after returning.
However, a better approach is to not pass your data objects directly to the view, but instead use a view model which you populate from the returned data objects.
Try this
#Html.Partial("MyPartialView", Model)

Connection between model, view and controller

I've searched a lot and read a lot about the concept of MVC. But I still don't know how to connect them together. Suppose I have a controller class, view class and model class. If user did something in that view, view should notify controller the action and controller may need to communicate with model to get some data.
Now, does view hold a reference to the controller? Does the controller has a property of that model? Or they just using like "include"?
The data is saved in memory, or database? Memory means stored in variables.
More complicated case, one action from user may need many controllers and models involved. How to co-ordinate them together?
What I've done before is I create a "view controller", which has view and some actions of that view.
And sometimes there is no model. All data is passed by parameter. If there are some models, some of them are singleton so I can get it everywhere.
This is a very generic answer, depending on your system these can vary:
The Controller has a reference to the View, an IBOutlet for example. The View is not aware of the Controller, it's a dumb thing that receives info and displays it, that's it.
Depends on what you have in place, sometimes you don't need to persist the data and being on variables is enough. For persisting data, you have CoreData, plists or save them on the sandbox.
Normally you could have a parent Controller, holding references to child Controllers. Each child should be independent and the parent would be the "glue" between them.
I can point you to this repo I created, called iOS Architecture, to help you understand how the controller and the model interact.
Here are some more points:
When your Action returns a View("SomeView"), it will check first if you have a layout, if not it will look for SomeView.cshtml in the Views folder of that controller.
If you want SomeView to talk to any Action in the controller, what you can do is to do a FormMethod.Post and assign the correct model and of course the Action name. That action name should be decorated with [HttpPost] or you can do a FormMethod.Get and decorate it with [HttpGet] or just leave it blank since [HttpGet] is default.
If you want another action redirecting first to another action without going to a page, you use RedirectToAction() instead of View and change the type to ActionResult instead of ViewResult
I hope it makes sense to you :)

I have a ViewModel strongly bound to my View in my ASP.NET MVC app, now what about the controller

So like the title says, I created a view model in my asp.net mvc application so it would be strongly typed to my view. My view model is a combination of two of my model classes. Now when the user hits the save button on that view, it goes to a controller. How does it know what controller to go to? I built my controller 1 - 1 so to speak with my models and views so controller A knows about Model A and controller B knows about Model B. But if I have a view model that is AB how does it know on subit to go to A or B. Do I need a controller called AB Controller?
The controller and action invoked do not depend on the viewmodel object you used to bind the page during initial view rendering. The controller (and action) invoked is determined by the URL of the request sent. (One of the routes you have defined will be matched based on the URL string of the request or a 404 not found error will be returned.)
A submit button in an HTML form (usually a POST) will have an action attribute that determines its url target, an anchor tag will have an href, etc.
In your situation where you have a custom viewmodel object you can define an action to expect and to attempt to parse that specific type of object by specifying it as the parameter to your action:
public ActionResult SaveSystemSetting(SystemAdminVM item) {
An Action Method doesn't receive a model. It receives parameters.
Anyway I think I know where you come from: One of the parameters of your action has the type of the ViewModel used in the View. That is a common layout and makes a lot of sense. In lots of projects it is so widely used that after some time you start to think that an action actually receives a model.
Just remenber that the parameters of your action can by anything that can by filled by the ModelBinder. You could pass both Models as parameters, you could pass a ViewModel that agregates Model a and b or you could pass a ViewModel that has properties of a + b.
Agregation ist the most common approach.
Generally we overload our action methods in the controller so if you have an action called edit that renders the view with your viewmodel object, you will have an overloaded edit action method with the HttpPost specified for that method.
The data to this method will be passed as form value collection values or you can bind it to your viewmodel object if you like and process it further.

How do I pass data from a controller to a strongly typed user control in asp.net mvc?

I have a strongly typed User Control which should show a user's orders on all pages, it's included in a master page. I have an OrdersController which can give me the current orders and is used at other locations.
How do I tell the UserControl in the Master Page that it should get its Data from that specific Controller / Controller Action? I'd like to access the viewdata within the ascx just as I would in a normal View.
Pass model and ViewData as parameters to the RenderPartial method.
It will make model and view data accessible as if you were in the parent view page.
<% Html.RenderPartial ( "../Shared/HRMasterData/DependentPersonDossiers",
ViewData.Model, ViewData ); %>
One way to do this would be to implement a base controller. Move the logic that obtains the orders to the base controller. Override OnActionExecuted in the base controller and check if the result is a ViewResult. If it is, find the orders and add them to the ViewData. In your master, check if the orders are present in the view data and, if so, render them. If not, show an "order data unavailable" message (this latter shouldn't happen, but it's best to be safe). Reuse (call) the code in the base controller in your OrdersController for the action that renders the orders view.

Resources