using object available to view, unavailable to partial view - asp.net-mvc

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)

Related

Should I always use a view model or is it ok to use ViewData?

when do you think is it better to use ViewData over a view model?
I have the same exact partial view in a couple of main views. I'd like to control how a partial view is rendered but I'd also prefer the partial view to accept only a view model which is a collection of records, just a pure IEnumerable<> object. I'd rather avoid to send the full view model object from a main view because it also contains a lot of different properties, objects, that control paging, sorting, filtering etc.
Therefore the question is if I should create another view model for the partial view or is it ok to use ViewData? I've read soemwhere that using ViewData is a very bad practice.
With View Data, I could simply pass require details like this:
#{
ViewDataDictionary vd = new ViewDataDictionary
{
new KeyValuePair<string,object>("WithDelete", Model.WithDelete),
new KeyValuePair<string,object>("WithRadarCode", Model.WithCode)
};
}
// ...
#if (Model != null) {
Html.RenderPartial("_ListOfRecordingsToRender", Model.recordingViewModel, vd);
}
At the moment, it would be sorted out.
My worry is that currently this *.recordingViewModel has plenty of different variations in my project, because of different models for creating / editting, listing, shoing details of a record etc. I feel like it may start to be too messy in my project if I make view model for each action.
What do you think. Please could you advice on that particular problem. Thanks
I think you should stick to using a ViewModel, your ViewModel is the class that defines your requirements for the view.
My reason behind this is that in the long run, it will be a lot more maintainable. When using ViewBag it's a dynamic class so in your views you should be checking if the ViewBag property exists (And can lead to silly mistakes like typo's) e.g.:
if(ViewBag.PropertyName != null)
{
// ViewBag.PropertyName is a different property to ViewBag.propertyName
}
This type of code can make your View's quite messy. If you use a strongly typed model, you should be able to put most of the logic in your controllers and keep the View as clean as possible which is a massive plus in my books.
You also will also end up (if you use ViewBag) attempting to maintain it at some point and struggle. You are removing one great thing about C#, it's a strongly typed language! ViewBag is not strongly typed, you may think you are passing in a List<T> but you could just be passing a string.
One last point, you also will lose out on any intellisense features in Visual Studio.
I feel like it may start to be too messy in my project if I make view model for each action.
Wont it just be as messy in your controllers assigning everything to a ViewBag? If it was a ViewModel you could send it off to a 'Mapping' class to map your DTO to your View.
Instead of this:
// ViewModel
var model = new CustomViewModel()
{
PropertyOne = result.FirstResult,
PropertyTwo = result.SecondResult,
}
//ViewBag
ViewBag.PropertyOne = result.FirstResult;
ViewBag.PropertyTwo = result.SecondResult;
You could do this:
var mapper = new Map();
var model = mapper.MapToViewModel(result);
*You would obviously need to provide an implimentation to the mapping class, look at something like Automapper
I'd also prefer the partial view to accept only a view model which is a collection of records, just a pure IEnumerable<> object. I'd rather avoid to send the full view model object from a main view because it also contains a lot of different properties, objects, that control paging, sorting, filtering etc.
That is fine, just create a view model that has a property of IEnumerable<T>. In my opinion you should try and use a strongly typed ViewModel in all of your scenarios.

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")

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 :)

PartialView causing error

I have created a partial view which returns a list of items where the ItemsId matches the one passed to it.
//
// GET: /ItemOptions/Item/5
public ActionResult Item(int id = 0)
{
var itemoptions = db.ItemOptions.Where(o => o.ItemsId == id);
return PartialView(itemoptions.ToList());
}
I am trying to display this on the details page of the parent item using the following code:
#Html.Partial("../ItemOptions/Item", Model.ItemsId)
If I visit {URL}/ItemOptions/Item/1 it returns the table I am expecting to see. But if I navigate to the parent item where I am trying to include the partial view I get the following error:
The model item passed into the dictionary is of type 'System.Int32', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[shopServer.Models.ItemOptions]'.
I have read other posts but can't work out where I am going wrong. I also read that I may need to use #Html.Action instead of Partial views, but I am unsure which is appropriate in which situation.
The method Html.Partial is directly rendering the partial view (without executing the controller logic), using the object passed in the second parameter as the model instance. So as the error says, the code line #Html.Partial("../ItemOptions/Item", Model.ItemsId) is trying to render your partial view using an integer (Model.ItemsId) as the model, but your partial view expects IEnumerable[shopServer.Models.ItemOptions].
You have 2 options for resolving your issue, depending if the data for the partial view is already loaded in the model for the parent view or not.
If in the parent view the model already contains the collection of item options, then you can just pass them to the partial view using the Html.Partial method. So assuming you could have a property like ItemOptions in the model of your parent view, then you could render the partial as:
#Html.Partial("../ItemOptions/Item", Model.ItemOptions)
If that is not possible, and the data you need for the partial view is unknown to the parent model, then you need to execute the controller logic (so you can populate the model for the partial view retrieving the extra data from the databaes.) In that case what you need is a child action, achived using the method Html.Action. You will need to pass the controller name, action name and parameters for the action method as in:
#Html.Action("Item", "ItemOptions", new {id = Model.ItemsId})
With the second option, the logic in the controller action method is executed, retrieving the ItemOptions from the database, rendering the partial view and including the resulting html into your main view.
You can find a quick rule of thumb for Html.Partial and Html.Action usage `here.
Hope that helps!

"Cannot Resolve Partial View X" in Umbraco 6 Html.Partial

I'm using Umbraco 6, and am using the new MVC architecture. I have a document type view template, which has a view model, which is instantiated and has it's properties populated from the controller. One of these properties is a collection, and in the view template I iterate through the collection and render out a Partial View with a separate view model, using 'Html.Partial("partialName", modelObject)'
The weird problem I'm having is that firstly, in Visual Studio I get a ReSharper warning telling me that it cannot resolve a partial view with that name (I have checked 50 times and I've spelt it correctly). Additionally when I then navigate to the page, I get the trusty ol' "Object reference not set to an instance of an object" YSOD.
I have debugged the code, and the controller action is hit fine, the logic to instantiate the view model for the document type template works fine, and populates the properties correctly, I've also made sure the properties are still set inside the view and the loop for rendering out the collection items correctly instantiates a view model object per collection item and sets the properties correctly. It breaks when it hits the Html.Partial.
Document Type View Code below:
#foreach (KeyValuePair<decimal, IPublishedContent> result in Model.Results)
{
PropertySearchResultViewModel model = ObjectMapper.SearchResultToViewModel(result);
Html.RenderPartial("PropertySearchResultDesktop", model);
}
Partial View Code below:
#using Production.Umbraco.Extensions.Models.ViewModels;
#inherits UmbracoViewPage<PropertySearchResultViewModel>
<article id="property-result-#Model.Node.Id.ToLower()">
<p>#Model.Node.Name</article>
<p>Distance: #Model.Distance Miles</p>
</article>
Here is a screenshot of my VS Solution tree:
The 'NewHomes.cshtml' document type view template is returned from the NewHomesController and the view is calling the 'PropertySearchResultDesktop.cshtml' partial view, which was created from the Umbraco back office, and was placed their automatically.
The Umbraco website says that you can and should place your partial views here
http://our.umbraco.org/Documentation/Reference/Mvc/partial-views
But no matter what I try to do, it just wont render the partial. I've seen one other question about this on SO but the answer was just to place it in MacroPartials instead, which I dont want to do as part of the benefit of using Partial Views in Umbraco 6 is that they inherit from UmbracoViewPage with a strongly typed model declaration, which MacroPartials don't.
Has anyone encountered this before?
Fixed. The issue was with the model I was passing to the document type view from the controller.
In the Umbraco documentation it says, you can create a controller to hijack the Umbraco route and serve up your own view with a custom model, like so:
public ActionResult Index(RenderModel model)
{
SearchResultsViewModel viewModel = new SearchResultsViewModel
return CurrentTemplate(viewModel);
}
and in my view I had:
#inherits UmbracoViewPage<SearchResultsViewModel>
However, it seems as though, in order to do that, you must make sure your custom view model in inherits from RenderModel with a constructor that takes RenderModel as a parameter and then sets some properties on the base object, like so:
public class SearchResultsViewModel :RenderModel
{
public SearchResultsViewModel(RenderModel model) : base(model.Content, model.CurrentCulture)
{
}
}
Previously, my view model had not inherited from anything and had a parameterless constructor.
This article led me to the right answer.
http://www.ben-morris.com/using-umbraco-6-to-create-an-asp-net-mvc-4-web-applicatio
Also, as a side note, I still get the ReSharper warning of "Cannot resolve partial view PropertySearchResultDesktop" but I think that's a ReSharper bug rather than an error.
Even with a full path and file extension in the call, it still complains.
I do find it odd though that while debugging, even with my old controller code, no exception was thrown at the model binding stage or inside the controller, or in the view until it go to the Html.Partial call.
Anyway, I hope this helps anyone having the same issue.

Resources