Does Razor ViewEngine cache the rendered HTMLs? - asp.net-mvc

I have a 2-level menu item: I have a list of department and each department has a list of stores.
I have a Menu, PartialView which iterates through the Model (departments) and builds the menu:
#model IEnumerable<Department>
<ul>
#foreach (var department in Model)
{
<li>
#Model.DepartmentName
<ul>
#foreach (var store in department.Stores)
{
<li>#store.StoreName</li>
}
</ul>
</li>
}
</ul>
And this is how I call the Menu PartialView in my _layout.cshtml:
#Html.Partial("Shared/_Menu", MyApplicationCache.departments)
As you can see I am passing the same model (from the cache) to the PartialView on all the requests.
Does Razor ViewEngine have an internal caching system to recognize that this view has already been built (complied to HTML string) for this model? Or does it re-render (recompile) the PartialView on every single request?

The PartialView gets re-rendered at every single request, assuming you don't have any OutputCacheAttribute applied on the Controller or its action method involved.
If you need output caching, you need to set this up explicitly via OutputCacheAttribute, see the documentation.
You can easily check this by outputting a DateTime, eg. via a menu-item as shown here below.
At every request, it will show a new value, proving it got re-rendered.
<li>#DateTime.Now</li>
Full menu:
#model IEnumerable<Department>
<ul>
#foreach (var department in Model)
{
<li>
#Model.DepartmentName
<ul>
<li>#DateTime.Now</li>
#foreach (var store in department.Stores)
{
<li>#store.StoreName</li>
}
</ul>
</li>
}
</ul>

Re-Rendering and Re-Compiling are very very different in ASP.Net MVC. While most of the answers here are correct, the View is only compiled once (except in debug mode, where it's compiled each time so you can change the view, hit refresh and see the change, or if the timestamp changes on the file in production). It is compiled into a runtime class that derives from WebViewpage(no ViewModel) or WebViewPage<T>(has ViewModel of type T).
The class is instantiated for each view needed (so if you used the same partial multiple times, you have to instantiate the each time), the model is populated, and the execute() method is called to create/stream the HTML to the client. The view could never be cached per model as it is to complicated to do that and instead the MVC team chose to allow configuring caching per controller method, not per WebViewPage.
#ErikPhilips, thanks a lot for this - So the View is only compiled once (no matter if we use or not use OutputCache)? It's the execute method which renders the runtime class into HtmlString, and it is the rendering which would benefit from caching?
Sorta of, but it's much more advanced, easier and complicated then that.
Advanced - The output cache is based on the controller method. So if the output cache configuration determines that the call can use a cached version, the controller method is never called. That's where the huge performance gain is. Imagine no call to the DB / external API call, there isn't a need. You could configure the cache so if it sees id=1 cache it for 30 minutes. Now anyone who calls that method with authorization and id=1 gets the cached string/html.
Easier - You put your OuputCacheAttribute on a method, configure it and you're done. Pretty darn easy to configure.
Complicated - Caching can be more complicated because you can render other controller methods using Html.Action() (Html.Partial() if you don't need a layout for your partial) or the preferred Html.RenderAction(); (Html.RenderPartial() if you don't need a layout). There use to be a Donut Hole Caching Issue (recommended reading) but that's been fixed for a long time.

This question has a great answer, which proves PartialViews are not cached, and this link which is suggested in the comments explains how to use [OutputCache] for a partialView - this could be used with Html.Action() / Html.RenderAction() and renders PartialViews as a [ChildAction].
Caching PartialView as a child action makes sense, but I did not want to render my menu as a [ChildAction], because I did not want to make a separate call for displaying the menu, so this is what I ended up doing:
I used RazorEngine to render my PartialView into HtmlString on application Startup, and will keep the HtmlString in a static variable (cache).
public static class MenuCache
{
private static readonly MvcHtmlString _menuMvcHtmlString;
static MenuCache()
{
using (var context = ApplicationDbContext.Create())
using (var razorEngine = RazorEngineService.Create(new TemplateServiceConfiguration()))
{
var repository = new MyRepository(context);
var departments = repository.GetDepartments();
// use razorEngine to render menu partial view into html string
// keep the htmlString in cache: _menuMvcHtmlString
string menuPartialView = File.ReadAllText(HostingEnvironment.MapPath("~/Views/Shared/_Menu.cshtml"));
string menuHtmlString = razorEngine.RunCompile(menuPartialView, "menuKey", null, departments);
_menuMvcHtmlString = new MvcHtmlString(menuHtmlString);
}
}
public static MvcHtmlString GetMenuHtmlString()
{
return _menuMvcHtmlString;
}
}
I also created a Customized HtmlHelper method which would return the HtmlString for the menu:
public static class HtmlHelperExtensions
{
public static MvcHtmlString MyMenu(this HtmlHelper html)
{
return MenuCache.GetMenuHtmlString();
}
}
Now in my _Layout page, I can use the customized HtmlHelper to display the menu:
#Html.MyMenu()

Related

ASP.NET MVC Generic Partial View Pattern

I have a partial view that I want to be generic. According to this question, partial views cannot be generic. So I instead made an HtmlHelper extension that handles the pieces for which I want type-safety, then hands off the rest to a real partial view.
Usually my helper is called on page load, which works fine, but sometimes I want to add a row or something through AJAX. When this happens, the controller cannot use my "partial view" since it does not have access to the HtmlHelper.
Apart from having a partial view with a model of type object, is there anything I can do?
I'm using Razor, if that is important.
A simplified version of what I'm doing:
public static MvcHtmlString DoStuff<T>(this HtmlHelper html, IEnumerable<T> data,
Func<T, ViewModelType> StronglyTypedFn, string PartialName)
{
// the pre- and post-processing for the partial view is complex enough I'd like
// to encapsulate it. But I want the encapsulation to include the safety
// benefits that generics give.
var mappedData = data.Select(StronglyTypedFn);
string htmlData = "";
foreach(var model in mappedData){
htmlData += html.Partial(PartialName, model);
}
htmlData += "some boilerplate footer html";
return htmlData;
}
I realize that in this example I have so few lines of code outside the partial view that it seems pointless to have a helper, but in my real example it is more complex.
Now, in an ajax call I want to return Html.DoStuff(). But I can't, because this requires access to the HtmlHelper, and the helper isn't available inside a controller.
You could just have a simple action method that calls the partial for one model instance
public PartialViewResult Single(string partialName) {
return PartialView(partialName);
}
You could use a View with a Dynamic type instead of object.
But... It seems as if there's some misunderstanding here because the Controller shouldn't try to render the view at all. Could you post the Controller code?
The better option is, IMO, returning a JsonResult for your ajax request and adding the row/rows on client side using JS.

ASP.NET MVC Master Page Data

The more I use ASP.NET MVC, the more I love it. However, in the case of showing model data on master pages there seems several ways of doing it. I am unsure as to the best solution.
My example would be a commerce site where I want to output a list of product categories on every page and also show the status of the visitors cart.
In asp.net web forms I would typically do this using user controls each doing their own databinding to retrieve the required data.
In MVC all data should be passed by the controller.
So regarding the categories the simplest solution would seem to be to pass this in View data in the controller action:
ViewData["Categories"] = _service.GetCategories();
However, doing this for every action isn't very DRY so following this article I created a base controller that adds the required data to my ViewData:
public class AppController : Controller
{
IAppService _service;
public AppController() { }
public AppController(IAppService appService)
{
_service = appService;
SetSiteData();
}
private void SetSiteData()
{
ViewData["Categories"] = _service.GetCategories();
}
}
I then created an extension for ViewMasterPage:
public static void RenderCategoryList(this ViewMasterPage pg) {
pg.Html.RenderPartial("CategoryList", pg.ViewData["Categories"]);
}
And in my MasterPage:
<div>
<%this.RenderCategoryList(); %>
</div>
This seems quite a clean approach. However, is this the best way as I have also seen suggestions of creating a ViewModel for your MasterPage. I could see that perhaps as your ViewModel data grows, this may be a better solution.
Regarding the cart status, I suppose I would do something similar but am unsure whether RenderAction would be more appropriate (When to use RenderAction vs RenderPartial with ASP.NET MVC).
Thanks,
Ben
That works, although it's not the way I would do it for 2 reasons:
I don't like sticking data into ViewState since you essentially cast it as object
By requiring a base controller you're limiting the functionality to controllers that inherit from this basecontroller (which might not be an issue though)
I think this would be a perfect use of RenderAction (part of the MvcFutures project). This helper can be used to render an Action on another controller. So you might have a ProductController with a ListCategories action, you could just do something like:
<% Html.RenderAction<ProductController>(x => x.ListCategories()); %>
ListCategories would call
_service.GetCategories();
and might stick the info into its own Model. Then it would pass that model to the View would would be a Partial Page.
Thank you - RenderAction was perfect the job.
I got more information from here.
So for the benefit of others, here is an example of how I am outputting cart status:
Action:
[ChildActionOnly]
public ActionResult CartStatus()
{
return PartialView(_service.GetCartSummary());
}
Partial View (bound to Models.Cart)
<div class="cartSummary">
<%if (Model.HasItems) { %>
Cart Items: <%=Model.Items.Count() %> | Total: <%=Model.TotalItems %>
<%} else {%>
Your cart is empty. Please buy stuff!
<%} %>
Helper method for Master Page:
public static void RenderCartStatus(this ViewMasterPage pg) {
pg.Html.RenderAction("CartStatus", "Catalog", null);
}
And finally master page:
<%this.RenderCartStatus(); %>
Thank you for the advice.

How do I know if the OutputCache for a given control is being used?

I am attempting to write a common "menu.ascx" usercontrol in Asp.Net MVC that will generate a properly formatted HTML menu for my application. The menu is generated based on content in the database and a series of Resource resolutions... which are passed to the PartialView through an attribute on a ViewModel.
It would make sense to utilize an OutputCache directive on the menu.ascx control in order to limit the number of round-trips to the database and Resource files. My intention is to mark the OutputCache directive with VaryByParam=none and VaryByCustom attributes, implementing a custom security lookup in global.asax...
My question is: how do we know when the OutputCache for menu.ascx is going to be used, so that we can skip the data fetch operations when constructing the ViewModel in the controller?
Some sample UserControl code:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%# OutputCache VaryByParam="none" VaryByCustom="customstring" %>
<ul>
<% var model = (IMyViewModel)Model;
foreach (var menu in model.Menus) { %>
<li><%= menu.Text %></li>
<% } %>
</ul>
Here's interesting readin on that subject Donut Hole Caching in ASP.NET MVC and here ASP.NET MVC Result Cache , I would basically do this menu by RenderAction method in master page to invoke action which will pull data from database and theb , and then cache the action result
I think I have found a suitable workaround to my problem.
In the Menus property getter for my concrete ViewModel implementation, I am writing proxy code to reach back into the instantiating Controller and request the Menu data. In this way, I can create the Menu data on-the-fly, when the PartialView requests it. If the PartialView is coming out of the OutputCache, the Menu property will not be requested.
So, my IMyViewModel looks a little like this:
public interface IMyViewModel {
IEnumerable<Menu> Menus { get; }
///<summary>
/// A pointer back to the calling controller, which inherits from the abstract MyBaseController
///</summary>
MyBaseController Controller { get; set; }
}
and my concrete implementation of Menus looks a little like this:
public IEnumerable<Menu> Menus
{
get { return Controller.GetMenus(); }
}
Comments? Is this a viable solution?

ASP.NET MVC: Should Controllers called by AJAX return JSON or rendered html?

I am having trouble deciding if a controller action, which is called by AJAX, should return a partial view, or the "raw" JSON.
Returning a partial view, with rendered HTML makes it easier for the javascript to simply update the current DOM with the returned HTML. However, it does limit what javascript client consuming the webservice can do with the returned HTML.
On the other-hand, having the controller action return JSON would require the javascript making the call to "manually" create the markup based on the JSON that is returned.
So as usual, each approach has it's benefits and weakness. Are there any other pros/cons for each approach?
In my opinion, returning JSON and then letting the client side view sort it out can be messy because of the following limitations:
No standard templating language for JavaScript. In the worst case scenario, you'll be tempted to concatenate strings to form the HTML that you require.
No easy way to unit test the HTML generated by your concatenation code.
Lack of IntelliSense for your JavaScript means that you're also prone to make more mistakes.
The way I've handled this is to return rendered HTML, BUT return this rendered HTML using a partial view instead. This gives you the best of both worlds. You've got Server-side templates as well as IntelliSense support.
Here's an example:
Here's my Ajax call, as you can see all it does is replace the html for my unordered list:
FilterRequests: function() {
$.post("/Request.aspx/GetFilteredRequests", { }, function(data) {
$('ul.requests').html(data);
});
},
Here's my action on my controller:
public ActionResult GetFilteredRequests(string filterJson)
{
var requests = _requestDao.LoadAll();
return PartialView("FilteredRequests", requests);
}
Finally here is my partial view (there's no need to understand this, I'm just showing you how complex some rendering can get in a real world application. I'd dread doing this in JavaScript. You'll also notice that my partial view in turn calls other partial views as well.):
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Request>>" %>
<%# Import Namespace="Diangy.HelpDesk.Models.Lookups"%>
<%# Import Namespace="Diangy.HelpDesk.Models.Requests"%>
<%# Import Namespace="System.Web.Mvc.Html"%>
<% foreach (var request in ViewData.Model) %>
<%{%>
<li class="request">
<h2>#<%= request.Id %>: <%= request.InitialInteraction().Description %></h2>
<p>from <%= request.Customer.FullName %> (<%= request.Customer.EmailAddress %>), <%= request.InitialInteraction().UsableTimeStamp %></p>
<h3>Custom Fields & Lookups</h3>
<div class="tabs">
<ul>
<li>Custom Fields</li>
<% foreach (var lookupDefinition in (List<LookupDefinition>)ViewData["LookupDefinitions"]) %>
<%{%>
<li><%= lookupDefinition.Name %></li>
<%}%>
</ul>
<% Html.RenderPartial("CustomFields", request); %>
</div>
<% Html.RenderPartial("Discussion", request); %>
<% Html.RenderPartial("Attachment", request); %>
</li>
<%}%>
If you are using the MVC paradigm, the controller should return data (JSON) and let the view sort it out for itself, just like its job is to find/adapt the data in the model and pass it on to the view on the server side.
You get browny points for
preserving separation of concerns between logic and UI
making your ajax actions testable (good luck testing the HTML returned from that action...)
It's a bit more complicated maybe, but it fits.
You can use client-templating systems such as what is now available in the MS Ajax Toolkit to help take some of the load and preserve the logic/rendering separation on the client side.
So I'd say JSON, definitely. But hey, YMMV as usual...
I like to allow the calling app to decide. I've peiced together a MultiViewController (much of the code I found online, I'll try to update with the credit when I find it) that, based on the action extension, will return the appropriate format. for example:
myapp.com/api/Users/1 - defaults to html based on route
myapp.com/api/Users.html/1 - html
myapp.com/api/Users.json/1 - json
myapp.com/api/Users.xml/1 - xml
myapp.com/api/Users.partial/1 - returns a partial view of action name (see code)
myapp.com/api/Users.clean/1 - partial html without styling, etc...
My controllers inherit from MultiViewController and instead of "return view(Model);" I simply call "return FormatView(Model); or FormatView("ViewName",Model);". The second if I need to apply a specific view to the result - not the implied view.
The MultiViewController looks like this. Pay special attention to FormatView, whitch returns an action result:
public abstract class MultiViewController : Controller
{
private const string FORMAT_KEY = "format";
public enum FileFormat {Html, Json, Xml, Partial, Clean}
protected MultiViewController()
{
RequestedFormat = FileFormat.Html;
}
protected FileFormat RequestedFormat { get; private set; }
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
var routeValues = filterContext.RouteData.Values;
if (routeValues.ContainsKey(FORMAT_KEY))
{
var requestedFormat = routeValues[FORMAT_KEY].ToString();
if (isValidFormat(requestedFormat))
{
RequestedFormat = (FileFormat)Enum.Parse(typeof(FileFormat), requestedFormat, true);
}
}
}
private bool isValidFormat(string requestedFormat)
{
return Enum.GetNames(typeof (FileFormat)).Any(format => format.ToLower() == requestedFormat.ToLower());
}
protected ActionResult FormatView(string viewName, object viewModel)
{
switch (RequestedFormat)
{
case FileFormat.Html:
if (viewName != string.Empty)
{
return View(viewName,viewModel);
}
return View(viewModel);
case FileFormat.Json:
return Json(viewModel);
case FileFormat.Xml:
return new XmlResult(viewModel);
case FileFormat.Partial:
//return View(this.ControllerContext.RouteData.Values["action"] + "Partial");
return PartialView(this.ControllerContext.RouteData.Values["action"] + "Partial");
case FileFormat.Clean:
if (viewName != string.Empty)
{
return View(viewName, "~/Views/Shared/Clean.master", viewModel);
}
var v = View(viewModel);
v.MasterName = "~/Views/Shared/Clean.Master";
return v;
default:
throw new FormatException(string.Concat("Cannot server the content in the request format: ", RequestedFormat));
}
}
protected ActionResult FormatView(object viewModel)
{
return FormatView("", viewModel);
}
}
Clean.master is simply a master page that doesn't contain any additional html - it takes the view (so that I can consolidate any partial classes) and renders it with clean html that can be placed directly.
If I want json - the controller builds my viewmodel and then returns that view model as json, instead of sending to the default view - the same with .xml.
Partial views are a little interesting in that, by convention, all of my main views are broken down into partials, so that particular partial can be requested by itself - this comes in handy for mimicking the functionality of an updatepanel using jquery without all the junk associated with the updatepanel.
To maintain seperation of concerns you should return JSON.
When you return html you limit what you can do with the returned data. If you need a list of data and want to present it in different ways use JSON, othewise you would have to have different methods on the server to get the different renderings of the same data.
Why not both json and html?
In current project we are making routes so you can choose from front end, which format is the best suited in some case...so why don't make two controllers, first will return expected data in json and the other controller will return same data but with html...this way you can choose from let's say jQuery what and when you want and in which format you want it...and the best thing is, for different format you just need to call different adress...
in other words, make it restful, baby! :)
cheers
As for me, I choose data-driven approach. Here is a small set of features for both:
Data-driven:
JSON from server (Controller sends model directly to response)
JS template binding (May take more time at client side)
Low bandwidth
load
It's just soooo sexy!
Html-driven:
HTML from server (Controller sends model to some partial View, it renders result and returns it - may take more time at server side)
No overload in JS binding
High bandwidth load
Not sexy, no no :)
So you can maintain MVC even with HTML-driven approach though it will be a bit harder.
If you use JQuery you should use JSON or XML because it's simpler to modify, but if your ajax call only returns items for an listbox for example you could also use html.
My personal preference is JSON or XML because use JQuery very often

Function in ASP.NET MVC

A function returns only one view.
what if I want to return multiple views in a function?
For example, I have this code:
Function Index() As ActionResult
Dim _news As DataTable = News.newsSelect()
Dim _announcement As DataTable = Announcement.SelectAnnouncement()
Return View()
End Function
I want to return _news and _announcement to be used in the aspx page. How would I do this?
Are you trying to show both sets at the same time? News and Announcements?
If so then why not implement either a PartialView or two PartialViews?
Then in your main view you can render them and pass the collection to the PartialViews?
There are heaps of samples on this and the one I recommend is in NerdDinner if you haven't already seen it.
I hope this helps. If you want sample code then let me know.
One simple way is just to have those two datasets sent in a ViewData element, which you can access in a field.
example:
ViewData["Elements"] = new SelectList(aElements, "Guid", "Name");
is consumed as:
<%= Html.DropDownList("Elements","Pick an element")%>
Also, I think that if you read between the lines of this blog post here you will find an elegant way of achieving what you want ;) but its a bit more involved..(only because you mentioned Views instead of just variables..
Quote:
We need to create our own implementation of IViewFactory. This
is responsible for locating and
creating an instance of an IView
(which both ViewPage and
ViewUserControl implement).
To “inject” (all you DI fans excuse me borrowing the term without
using a DI framework) our new View
Factory into every Controller we are
going to create our own
IControllerFactory implementation.
We need to configure the framework to use our new Controller
Factory.
Finally we can create two Views – an AJAX version and a pure
HTML version.
Building on that should be all you need
Good luck!
Ric
Assuming what you are trying to do is use both of those DataTables to populate some View, then my recommendation would be to create a wrapper object and then a strongly typed view based on this object.
The wrapper object would contain properties for all of the data elements that you need in order to render your view properly. In your case, it is 2 DataTable objects. I do not really know VB, so all my examples will be in C#. Here is an example of the data wrapper class...
public class IndexViewData
{
public DataTable News { get; set; }
public DataTable Announcement { get; set; }
}
You then might update the Index action in your controller as follows:
public ActionResult Index()
{
var viewData = new IndexViewData();
viewData.News = News.newsSelect();
viewData.Announcement = Announcement.SelectAnouncement();
return View(viewData);
}
Finally, you would need to create/update your view to be strongly typed. This is done by having your page inherit from the generic System.Web.Mvc.ViewPage<T> class. Just substitute the view data wrapper created earlier for T. To do this, you would set the inherits attribute of the <%# Page %> element. In your case, if we assume your root namespace is called "Foo", you might have the following page declaration in your Index.aspx view (added extra line breaks for readability):
<%# Page Title=""
Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<Foo.Models.MyModelType.IndexViewData>"
%>
Once you have a strongly typed view for your view data wrapper, you can access the wrapper object in your view using the Model property. Here is an example of something you could do in your Index.aspx view
<%-- Output some random data (bad example, for demonstration only) --%>
<%= Model.News[0]["title"] %><br/>
<%= Model.Anouncement[0]["body"] %>
In reality you're probably going to do something like iterate over each row of the data table. Regardless, once you create the strongly typed view, your model object, which was passed to the view in the Index method of the controller, is available in the Model property within the view.
You can find detailed tutorials at the ASP.NET MVC site

Resources