Of Code and Me

Somewhere to write down all the stuff I'm going to forget and then need

Donut caching in asp.net mvc 2 with Html.Action April 8, 2010

Filed under: Asp.Net,C#,MVC,Uncategorized,Web — Rupert Bates @ 11:22 am

[Update – ok, so this officially doesn’t work, which sucks. I’m having a look at alternatives, but at the moment it looks like that is going to be reverting to MVC 1.0]

To do donut caching in Asp.Net Mvc 1.0 required quite a bit of hacking (or at least that was the only way I could get it to work), but with version 2 things get much better because of the addition of the Html.Action method.

As a quick recap, ‘donut caching’ is when you cache the whole of a web page except for small parts within it – the holes in the donut. A classic use for it is pages which have some personalised aspect like a logged in user name but are otherwise identical for all users.

To solve exactly this scenario I used the following code:

A child action called LoggedInUserInfo in my HomeController

        [ChildActionOnly]
        public ActionResult LoggedInUserInfo(HttpContextBase context)
        {
            return View(context);
        }

with a view of the same name:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<HttpContextBase>" %>
<%@ Import Namespace="KableDirect.Service" %>
<%@ Import Namespace="KableDirect.Web.FrontEnd.Models.Shared"%>

    <ul>
        <li>You are logged in as <%= Model.User.Identity.Name %> </li>

        <li><%=Html.ActionLink("My Profile", "Profile", "Account")%></li>

        <%if (Model.User.IsAdmin()){%>
        <li><a href="<%= RouteHelper.GetAdminUrl(Model)%>">Admin</a></li>
        <%}%>
        <li><%=Html.ActionLink("Logout", "Logoff", "Account")%></li>
    </ul>

I also have the following HtmlHelper method to make calling the asp.net WriteSubstitution() method easier:

    public delegate string MvcCacheCallback(HttpContextBase context);
    public static class CacheHelper
    {
        public static object Substitute(this HtmlHelper html, MvcCacheCallback cb)
        {
            html.ViewContext.HttpContext.Response.WriteSubstitution(c => cb(new HttpContextWrapper(c)));
            return null;
        }
    }

Now I can call my child action within my MasterPage using Html.Action as follows:

<%=  Html.Substitute(context => Html.Action("LoggedInUserInfo", "Home", new {context}).ToString()) %>

It is important that your action takes the context as a parameter or it will only be called once the first time the page is hit and then get cached along with everything else.
It is also important that you have a route in your route table that will catch the {controller}/{action} url pattern. See this post for more details.

Phill Haack now has a warning on his site about problems with donut caching in Asp.Net Mvc 2, but this has worked fine for me, I’ll update here if I find scenarios where this doesn’t work.

There is a good introduction to Html.Action here.

Advertisements
 

links for 2010-03-26 March 26, 2010

Filed under: Uncategorized — Rupert Bates @ 10:05 pm
 

Track file downloads with Google Analytics and JQuery February 26, 2010

Filed under: Uncategorized — Rupert Bates @ 3:33 pm

http://www.carronmedia.com/extend-google-analytics-with-jquery/

 

Fix the problem where Windows explorer opens a new window for every folder you click on rather than just opening it in existing window February 18, 2010

Filed under: Error,Uncategorized,Windows — Rupert Bates @ 10:25 am

click Start > Run… and paste this command: regsvr32 /i shell32.dll

 

301 redirects from Asp.Net February 9, 2010

Filed under: Asp.Net,guardian.co.uk,Uncategorized,Web — Rupert Bates @ 2:29 pm

Using Response.Redirect in Asp.Net will issue a 302 (temporary redirect) response. For items that have moved permanently you should issue a 301 (permanent redirect) using code such as:

<script runat="server">
private void Page_Load(object sender, System.EventArgs e)
{
    Response.StatusCode = (int) HttpStatusCode.MovedPermanently;
    Response.Status = "301 Moved Permanently";
    Response.AddHeader("Location", "http://www.my-redirect.co.uk/newhomepage.aspx");
}
</script> 

Paul Roach the SEO lead at guardian.co.uk explains why this matters:
“301 redirects are preferable from an SEO point of view because they instruct the search engine to pass all of the value of the original page to the destination page, including link equity and page history / age. If you use 302 redirects the search engine considers it a temporary state and does not pass the value from the original page to the destination page, this can also lead to both pages being displayed in the index.”

 

Ruby’s ‘times()’ function in C# January 24, 2010

Filed under: C#,Coding,Uncategorized — Rupert Bates @ 10:20 pm

I’ve seen a lot of people enthuse about Ruby’s times() function which allows you to write code like:

5.times { |i| puts i }

which prints out:
0
1
2
3
4
It is a really nice syntax and it struck me that you could use similar syntax in C# by defining the following extension method on int:

public static class IntExtensions
{
	public static void Times(this int i, Action<int> func)
	{
		for(int j = 0; j < i; j++)
		{
			func(j);
		}
	}
}

Which then lets you write:

5.Times(i => Console.Write(i));

with the same results

 

Create a UrlHelper from a library class in asp.net mvc January 7, 2010

Filed under: Uncategorized — Rupert Bates @ 11:13 am

If you need to get hold of a UrlHelper outside a controller, for instance to work out a url from an action in a library class, you can use the following function:

		public static UrlHelper GetUrlHelper()
		{
			var httpContext = new HttpContextWrapper(HttpContext.Current);
			return new UrlHelper( new RequestContext(httpContext, CurrentRoute(httpContext)));
		}
		public static RouteData CurrentRoute(HttpContextWrapper httpContext)
		{
			return RouteTable.Routes.GetRouteData(httpContext);
		}