MVC3 RenderPartial caching across multiple pages - asp.net-mvc

Can anyone tell me if its possible to Cache a RenderPartial across multiple pages? I have a RenderPartial for a user profile that shouldn't really ever change unless the user updates their profile. So i dont really want to go back and get his/her profile every time i load a page. I would much rather pass the partial around until im forced to update (i.e. profile update)
I looked at the DonutHole example that p.haack put together, but it seems to be relevant for a single page. Can someone point me in the right direction or offer any advice? Or am i only able to cache one page at a time? thanks!

You could use RenderAction instead. Example:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
return View();
}
[OutputCache(Duration = 6000, VaryByParam = "none")]
public ActionResult Cached()
{
// You could return whatever you want here, even a view
// but for the purpose of the demonstration I am simply
// returning a dynamic string value
return Content(DateTime.Now.ToLongTimeString(), "text/html");
}
}
and inside the Index.cshtml and About.cshtml views you could include the child action:
<div>
#{Html.RenderAction("Cached");}
</div>
and it you will get the cached version of it in both pages.

Related

MVC Linking to views

New to MVC so learning (in a frustrated kind of way as finding simple things that used to take 5 minutes in webforms have become over complicated)
So I've set up a solution in VS2015 which comes with the pre-requisite Account section.
I've also set up tables in a database and built the entities from that (producing some CRUD)
So I can log in and get to my AccountHome view (return View("AccountHome") in the AccountController)
From my AccountHome page, I can navigate to another view using a seperate controller(#Html.ActionLink("Add Foods", "Create", "fad_userFoods", new { id = ViewBag.userID }, new { #class = "btn btn-default" }))
Here I do some form entry and the data is successfully added to the database but here is where my problem lies. After data entry I want to go back to the AccountHome view
So in my fad_userFoodsControler I have this code(return RedirectToAction("AccountHome","Account")) - says resource cannot be found!!!
In my AccountController I have the following code:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AccountHome()
{
return View();
}
In my AccountViewModels I have this code:
public class AccountHomeViewModel
{
public string Email { get; set; }
}
Finally, in my AccountHome.cshtml page I have this declared at the top (#model FiveADayMVC2.Models.AccountHomeViewModel)
Where am I going wrong? Looked at numerous posts and they all do it this way. I know the view exists because I can get to it from login. Yet, if I type the url manually (i.e. my url followed by /Account/AccountHome) it's not found either???
It's going to be a lack of understanding on my part but where?
Thanks in advance for any pointers
Try using RedirectToAction This will navigate the user to the view of a specific action in a given controller.
if(actionSuccess)
{
return RedirectToAction("Index", "Home");
}
UPDATE:
Make sure you have a controller for HttpGet
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AccountHome()
{
return View();
}
public ActionResult AccountHome()
{
return View();
}
Since you added [HttpPost] to the controller it will be the one executed after a post was made on "AccountHome"

MVC detect when model is empty

I'm new to MVC, so I'm trying to figure out some best practices.
Suppose I have a controller HomeController method Index(MyViewModel model):
public ActionResult Index(MyViewModel model)
{
//if loading the page for the first time, do nothing
//if the page has been posted data from somewhere, then I want to use
// some of the arguments in model to load other data, like say search results
}
When I navigate to the /Index page, I (myself) expect the model object to come through as null, but it doesn't. MVC (somehow) creates a MyViewModel for me.
My question is, what's the best way or most consistent to determine if model was created automatically, or via a post?
Ideas:
Create a property on MyViewModel that gets set when the view is posting back
Check for if the Request.HttpMethod == "GET" or "POST"
Something else?
You should use different actions for your GET and POST requests. Don't try and make a single method do too much.
[HttpGet]
public ActionResult Index()
{
// handle the GET request
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
if (ModelState.IsValid)
{
// it's a post and the data is valid
}
}
The correct method will then be called depending on whether it's a GET or POST
Create two actions, one which accepts a model instance and one which doesn't.
Even though you're "going to the same page" you are in fact performing two distinctly different actions. The first action loads an initial page, the second action posts some value to be acted upon. Two actions means two methods:
[HttpGet]
public ActionResult Index()
{
// perform any logic, but you probably just want to return the view
return View();
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// respond to the model in some way
return View(model);
// or return something else? a redirect? it's up to you
}
Note that this kind of breaks your restful URLs. Consider semantically what you're doing in these actions:
Viewing an index
Posting to an index
The first one makes sense, but the second one probably doesn't. Normally when you POST something you're doing something related to a model or action of some sort. "Index" doesn't really describe an action. Are you "Create"-ing something? Are you "Edit"-ing something? Those sound like more meaningful action names for the POST action.

MVC _Layout with Action using Session -order of loading is an issue for me

All,
I have an MVC _Layout.cshtml that calls:
#Html.Action("GetActionStrip", "Vehicles")
I also have a Controller that loads some views.
My Issue is that I have a controller action called GetVehicleDetails which gets a vehicle by ID.
The Action on:
#Html.Action("GetActionStrip", "Vehicles")
requires that GetVehicleDetails loads first as it need to put a vehicle id into session.
This is not working as:
#Html.Action("GetActionStrip", "Vehicles")
Loads before GetVehicleDetails.
#Html.Action("GetActionStrip", "Vehicles")
Needs to be on multiple views, that's why I put it in the _Layout file.
I can get it to work by putting:
#Html.Action("GetActionStrip", "Vehicles")
On every view I need it on and then they load in the correct order. ie.. the controller action GetVehicleDetails sets the vehicle id into session and then:
#Html.Action("GetActionStrip", "Vehicles")
Reads the session value.
Has anyone got any idea if I can do it the way I want or will I have to put my #Html.Action on every view which kind of breaks the DRY principle.
thanks
RuSs
Paul, I tried to write you a comment but the character limit killed me. Here is my comment:
Paul,
Thanks for the message. I understand what you have written but before I continue and try to implement something like this I just want to be sure you understand, fully, the scenario.
Will YOUR scenario cater for the fact that the code that needs the session value is called from an #Html.Action in my _Layout (master page so to speak) whereas I need my controller get action to receive a parameter and set this parameter into session.
From what I understand, _Layouts (master pages) load first so my #Html.Action would run and look for the session value. But, as this code is in a _Layout, it would run first and hence the GET on my controller has not yet set the session from the actions passed in parameter.
Note: my _Layout doesnt have it's own controller (not sure if this matters)
Thanks
RuSs
Something about the design is fundamentally incorrect. You shouldn't have different components being tightly coupled like this. The order shouldn't matter for which one comes first.
Here is how I might do what you're looking for. I'd create a model bound class that you can receive in your controller actions where you need the session value. The model will pull the session value from the database or wherever if it hasn't been set yet otherwise it uses the session value. Now order doesn't matter. Better yet you could make MySessionObject an interface and then you can mock it out in your test cases.
public interface IMySessionObject
{
int GetValueX();
}
public class MySessionObject : IModelBinder, IMySessionObject
{
private HttpContextBase _httpContext;
private MySessionObject(HttpContextBase httpContext)
{
_httpContext = httpContext;
}
public int GetValueX()
{
if (_httpContext.Session["x"] == null)
{
_httpContext.Session["x"] = 54; // Get the value here.
}
return (int)_httpContext.Session["x"];
}
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var context = controllerContext.HttpContext;
var obj = new MySessionObject(context);
return obj;
}
}
public class HomeController : Controller
{
public ActionResult Index(IMySessionObject obj)
{
ViewBag.X = obj.GetValueX();
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}
public ActionResult About(IMySessionObject obj)
{
ViewBag.Message = "Your app description page.";
return View();
}
public ActionResult Contact(IMySessionObject obj)
{
ViewBag.Message = "Your contact page.";
return View();
}
}
Thanks Paul. I think your answer is correct without Sitecore CMS being part if the scenario. Ill vote you up but still doesn't fix my issue. Here is my other post which explains it better.
I don't expect you use Sitecore. https://stackoverflow.com/questions/14867915/order-of-loading-layout-and-url-driven-action-is-opposite-to-a-standard-non-sit ill try to find a way for Sitecore NOT to declaratively load my _layout before my MVC code runs.

Specifying view to load in Edit action

I'm working on my first ASP.NET MVC 3 application and I've got two Controllers with Views, IceCreamController/IceCreamView and RecipeController/RecipeView, and on each the user can make a selection and display the recipe.
Selection causes a PartialView to be displayed which an Edit link on it. When clicked the EditView for this recipe is displayed, allowing the user to edit the attributes of the recipe item selected.
Great. This works fine except currently the POST action in the RecipeController looks like so:
[HttpPost]
public ActionResult Edit(RecipeViewModel viewModel)
{
// updates the underlying model with the viewModel
// other things not germane to the discussion
return View();
}
and that ends up always showing the Index view for Recipe, which isn't what I want. Rather, I'd like to be able to do is send the user back to the appropriate View (IceCreamView or RecipeView) when they've submitted their changes.
I assume that others have done something similar to this. How do you communicate which Controller/Action should be redirected to when the Edit is done?
Note:
I added a bit above to clarify that I've got two separate Controllers (IceCreamController and RecipeController) and each has a View that can select and ultimately do a
#Html.Partial("_Recipe", model.recipe)
to display the details of a particular recipe. My problem is how to get the page redirected back to either IceCreamView or RecipeView by the Edit Action on RecipeController - essentially, how do I communicate where it should go since the recipe details could have been displayed by either path.
Solution Employed:
As you can read below in the comments to Darrin's answer, since I've got more than a single controller involved, a solution is to utilize the viewmodel to pass in the controller/action that should be redirected to following when the Edit post action is completed.
As I've got more than a single instance of this situation (arriving at an Edit page via multiple paths), I think creating a simple BaseViewModel to hold this functionality might be in order and then have all the applicable viewmodels inherit from that BaseViewModel.
I'm don't think it needs to be anything more than something like:
public BaseViewModel
{
public BaseViewModel(string controller, string action)
{
ControllerName = controller ?? string.empty;
ActionName = action ?? string.empty;
}
public string ControllerName { get; set; }
public string Action { get; set; }
}
And then a viewmodel's constructor could just be modified to pass in the controller/action and hand that off to the base class.
There may be other solutions to this and if so, I'd like to hear them.
[HttpPost]
public ActionResult Edit(RecipeViewModel viewModel)
{
// updates the underlying model with the viewModel
// other things not germane to the discussion
return View("IceCreamView");
}
or if you wanted to redirect you could have a controller action that would serve this view and then return RedirectToAction("IceCream"); which is probably more correct rather than directly returning a view from a POST action in case of success.

RESTful Controllers with Different Http Methods, But the Same Parameters

Let's say I have a Controller that handles a CRUD scenario for a 'Home'. The Get would look something like this:
[HttpGet]
public ActionResult Index(int? homeId)
{
Home home = homeRepo.GetHome(homeId.Value);
return Json(home, JsonRequestBehavior.AllowGet);
}
So far so good. Then I add a post action for adding new ones.
[HttpPost]
public ActionResult Index(Home home)
{
//add the new home to the db
return Json(new { success = true });
}
Awesome. But when I use the same scheme to handle puts (updating an existing home)...
[HttpPut]
public ActionResult Index(Home home)
{
//update existing home in the db
return Json(new { success = true });
}
We run into a problem. The method signatures for Post and Put are identical, which of course C# doesn't like. I could try a few things, like adding bogus parameters to the signature, or changing the method names to directly reflect CRUD. Those are hacky or undesirable, though.
What is the best practice for going about preserving RESTful, CRUD style controllers here?
This is the best solution that I know of:
[HttpPut]
[ActionName("Index")]
public ActionResult IndexPut(Home home)
{
...
}
Basically the ActionNameAttribute was created to deal with these scenarios.
HttpPut and HttpDeletes are restricted by some firewalls so at times simply HttpPost and HttpGet are used. If a record ID is passed in (or some other criteria) you know its an update. Granted - this is for you to determine, httpput may work just fine for you, this is just a warning on it, it usually isn't a big deal.
Either method used - beware of users trying to inject false IDs into the page in order to forcing updates of records they don't have access to. I get around this issue by hashing in this case home.HomeId on the view when we render it
ViewData["IdCheck"] = Encryption.ComputeHash(home.HomeId.ToString());
in your view:
<%: Html.Hidden("IdCheck", ViewData["IdCheck"]) %>
in your HttpPost or HttpPut method (whichever is doing the update)
if (Encryption.ComputeHash(home.HomeId.ToString()) != (string)Request.Form["IdCheck"])
{
throw new Exception("Hashes do not match");
}
Again - this same security issue exists no matter which method you use to do your update if you are trusting form data.

Resources