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
 

An extended Html.Encode function for the Asp.Net Mvc HtmlHelper February 12, 2010

Filed under: Asp.Net,C#,MVC — Rupert Bates @ 2:52 pm

The built in Asp.Net Html encoding function misses out a large number of extended characters (including apostrophes). This extension method for the HtmlHelper class does a much more thorough job:

using System.Collections.Generic;
using System.Web.Mvc;

namespace KableDirect.Web.FrontEnd.Helpers
{
    public static class HtmlHelperExtensions
    {
        #region The List of replacements

        public static readonly Dictionary<string, string> Replacements = new Dictionary<string, string>
                                                                             {
                                                                                 {"–", "&#8211;"},
                                                                                 {"—", "&#8212;"},
                                                                                 {"‘", "&#8216;"},
                                                                                 {"’", "&#8217;"},
                                                                                 {"‚", "&#8218;"},
                                                                                 {"“", "&#8220;"},
                                                                                 {"”", "&#8221;"},
                                                                                 {"„", "&#8222;"},
                                                                                 {"†", "&#8224;"},
                                                                                 {"‡", "&#8225;"},
                                                                                 {"•", "&#8226;"},
                                                                                 {"…", "&#8230;"},
                                                                                 {"‰", "&#8240;"},
                                                                                 {"€", "&#8364;"},
                                                                                 {"™", "&#8482;"},
                                                                                 {"'", "&#39;"},
                                                                                 {"ą", "&#261;"},
                                                                                 {"Ą", "&#260;"},
                                                                                 {"ę", "&#281;"},
                                                                                 {"Ę", "&#280;"},
                                                                                 {"Ā", "&#256;"},
                                                                                 {"ā", "&#257;"},
                                                                                 {"Ă", "&#258;"},
                                                                                 {"ă", "&#259;"},
                                                                                 {"Ǟ", "&#478;"},
                                                                                 {"ǟ", "&#479;"},
                                                                                 {"Ǻ", "&#506;"},
                                                                                 {"ǻ", "&#507;"},
                                                                                 {"Ǽ", "&#508;"},
                                                                                 {"ǽ", "&#509;"},
                                                                                 {"Ḃ", "&#7682;"},
                                                                                 {"ḃ", "&#7683;"},
                                                                                 {"Ć", "&#262;"},
                                                                                 {"ć", "&#263;"},
                                                                                 {"Č", "&#268;"},
                                                                                 {"č", "&#269;"},
                                                                                 {"Ĉ", "&#264;"},
                                                                                 {"ĉ", "&#265;"},
                                                                                 {"Ċ", "&#266;"},
                                                                                 {"ċ", "&#267;"},
                                                                                 {"Ḑ", "&#7696;"},
                                                                                 {"ḑ", "&#7697;"},
                                                                                 {"Ď", "&#270;"},
                                                                                 {"ď", "&#271;"},
                                                                                 {"Ḋ", "&#7690;"},
                                                                                 {"ḋ", "&#7691;"},
                                                                                 {"Đ", "&#272;"},
                                                                                 {"đ", "&#273;"},
                                                                                 {"DZ", "&#497;"},
                                                                                 {"Dz", "&#498;"},
                                                                                 {"dz", "&#499;"},
                                                                                 {"DŽ", "&#452;"},
                                                                                 {"Dž", "&#453;"},
                                                                                 {"dž", "&#454;"},
                                                                                 {"Ě", "&#282;"},
                                                                                 {"ě", "&#283;"},
                                                                                 {"Ē", "&#274;"},
                                                                                 {"ē", "&#275;"},
                                                                                 {"Ĕ", "&#276;"},
                                                                                 {"ĕ", "&#277;"},
                                                                                 {"Ė", "&#278;"},
                                                                                 {"ė", "&#279;"},
                                                                                 {"Ʒ", "&#439;"},
                                                                                 {"ʒ", "&#658;"},
                                                                                 {"Ǯ", "&#494;"},
                                                                                 {"ǯ", "&#495;"},
                                                                                 {"Ḟ", "&#7710;"},
                                                                                 {"ḟ", "&#7711;"},
                                                                                 {"ƒ", "&#402;"},
                                                                                 {"ff", "&#64256;"},
                                                                                 {"fi", "&#64257;"},
                                                                                 {"fl", "&#64258;"},
                                                                                 {"ffi", "&#64259;"},
                                                                                 {"ffl", "&#64260;"},
                                                                                 {"ſt", "&#64261;"},
                                                                                 {"Ǵ", "&#500;"},
                                                                                 {"ǵ", "&#501;"},
                                                                                 {"Ģ", "&#290;"},
                                                                                 {"ģ", "&#291;"},
                                                                                 {"Ǧ", "&#486;"},
                                                                                 {"ǧ", "&#487;"},
                                                                                 {"Ĝ", "&#284;"},
                                                                                 {"ĝ", "&#285;"},
                                                                                 {"Ğ", "&#286;"},
                                                                                 {"ğ", "&#287;"},
                                                                                 {"Ġ", "&#288;"},
                                                                                 {"ġ", "&#289;"},
                                                                                 {"Ǥ", "&#484;"},
                                                                                 {"ǥ", "&#485;"},
                                                                                 {"Ĥ", "&#292;"},
                                                                                 {"ĥ", "&#293;"},
                                                                                 {"Ħ", "&#294;"},
                                                                                 {"ħ", "&#295;"},
                                                                                 {"Ĩ", "&#296;"},
                                                                                 {"ĩ", "&#297;"},
                                                                                 {"Ī", "&#298;"},
                                                                                 {"ī", "&#299;"},
                                                                                 {"Ĭ", "&#300;"},
                                                                                 {"ĭ", "&#301;"},
                                                                                 {"Į", "&#302;"},
                                                                                 {"į", "&#303;"},
                                                                                 {"İ", "&#304;"},
                                                                                 {"ı", "&#305;"},
                                                                                 {"IJ", "&#306;"},
                                                                                 {"ij", "&#307;"},
                                                                                 {"Ĵ", "&#308;"},
                                                                                 {"ĵ", "&#309;"},
                                                                                 {"Ḱ", "&#7728;"},
                                                                                 {"ḱ", "&#7729;"},
                                                                                 {"Ķ", "&#310;"},
                                                                                 {"ķ", "&#311;"},
                                                                                 {"Ǩ", "&#488;"},
                                                                                 {"ǩ", "&#489;"},
                                                                                 {"ĸ", "&#312;"},
                                                                                 {"Ĺ", "&#313;"},
                                                                                 {"ĺ", "&#314;"},
                                                                                 {"Ļ", "&#315;"},
                                                                                 {"ļ", "&#316;"},
                                                                                 {"Ľ", "&#317;"},
                                                                                 {"ľ", "&#318;"},
                                                                                 {"Ŀ", "&#319;"},
                                                                                 {"ŀ", "&#320;"},
                                                                                 {"Ł", "&#321;"},
                                                                                 {"ł", "&#322;"},
                                                                                 {"LJ", "&#455;"},
                                                                                 {"Lj", "&#456;"},
                                                                                 {"lj", "&#457;"},
                                                                                 {"Ṁ", "&#7744;"},
                                                                                 {"ṁ", "&#7745;"},
                                                                                 {"Ń", "&#323;"},
                                                                                 {"ń", "&#324;"},
                                                                                 {"Ņ", "&#325;"},
                                                                                 {"ņ", "&#326;"},
                                                                                 {"Ň", "&#327;"},
                                                                                 {"ň", "&#328;"},
                                                                                 {"ʼn", "&#329;"},
                                                                                 {"Ŋ", "&#330;"},
                                                                                 {"ŋ", "&#331;"},
                                                                                 {"NJ", "&#458;"},
                                                                                 {"Nj", "&#459;"},
                                                                                 {"nj", "&#460;"},
                                                                                 {"Ō", "&#332;"},
                                                                                 {"ō", "&#333;"},
                                                                                 {"Ŏ", "&#334;"},
                                                                                 {"ŏ", "&#335;"},
                                                                                 {"Ő", "&#336;"},
                                                                                 {"ő", "&#337;"},
                                                                                 {"Ǿ", "&#510;"},
                                                                                 {"ǿ", "&#511;"},
                                                                                 {"Œ", "&#338;"},
                                                                                 {"œ", "&#339;"},
                                                                                 {"Ṗ", "&#7766;"},
                                                                                 {"ṗ", "&#7767;"},
                                                                                 {"Ŕ", "&#340;"},
                                                                                 {"ŕ", "&#341;"},
                                                                                 {"Ŗ", "&#342;"},
                                                                                 {"ŗ", "&#343;"},
                                                                                 {"Ř", "&#344;"},
                                                                                 {"ř", "&#345;"},
                                                                                 {"ɼ", "&#636;"},
                                                                                 {"Ś", "&#346;"},
                                                                                 {"ś", "&#347;"},
                                                                                 {"Ş", "&#350;"},
                                                                                 {"ş", "&#351;"},
                                                                                 {"Š", "&#352;"},
                                                                                 {"š", "&#353;"},
                                                                                 {"Ŝ", "&#348;"},
                                                                                 {"ŝ", "&#349;"},
                                                                                 {"Ṡ", "&#7776;"},
                                                                                 {"ṡ", "&#7777;"},
                                                                                 {"ſ", "&#383;"},
                                                                                 {"Ţ", "&#354;"},
                                                                                 {"ţ", "&#355;"},
                                                                                 {"Ť", "&#356;"},
                                                                                 {"ť", "&#357;"},
                                                                                 {"Ṫ", "&#7786;"},
                                                                                 {"ṫ", "&#7787;"},
                                                                                 {"Ŧ", "&#358;"},
                                                                                 {"ŧ", "&#359;"},
                                                                                 {"Ũ", "&#360;"},
                                                                                 {"ũ", "&#361;"},
                                                                                 {"Ů", "&#366;"},
                                                                                 {"ů", "&#367;"},
                                                                                 {"Ū", "&#362;"},
                                                                                 {"ū", "&#363;"},
                                                                                 {"Ŭ", "&#364;"},
                                                                                 {"ŭ", "&#365;"},
                                                                                 {"Ų", "&#370;"},
                                                                                 {"ų", "&#371;"},
                                                                                 {"Ű", "&#368;"},
                                                                                 {"ű", "&#369;"},
                                                                                 {"Ẁ", "&#7808;"},
                                                                                 {"ẁ", "&#7809;"},
                                                                                 {"Ẃ", "&#7810;"},
                                                                                 {"ẃ", "&#7811;"},
                                                                                 {"Ŵ", "&#372;"},
                                                                                 {"ŵ", "&#373;"},
                                                                                 {"Ẅ", "&#7812;"},
                                                                                 {"ẅ", "&#7813;"},
                                                                                 {"Ỳ", "&#7922;"},
                                                                                 {"ỳ", "&#7923;"},
                                                                                 {"Ŷ", "&#374;"},
                                                                                 {"ŷ", "&#375;"},
                                                                                 {"Ÿ", "&#376;"},
                                                                                 {"Ź", "&#377;"},
                                                                                 {"ź", "&#378;"},
                                                                                 {"Ž", "&#381;"},
                                                                                 {"ž", "&#382;"},
                                                                                 {"Ż", "&#379;"},
                                                                                 {"ż", "&#380;"}
                                                                             };
        #endregion

        /// <summary>
        /// A fuller html encoding than the built in method, will encode a large number of characters ignored by the built in method including apostrophes.
        /// </summary>
        /// <param name="helper"></param>
        /// <param name="html"></param>
        /// <returns></returns>
        public static string EncodeFull(this HtmlHelper helper, string html)
        {
            var result = helper.Encode(html);
            foreach (var replacement in Replacements)
            {
                result = result.Replace(replacement.Key, replacement.Value);
            }
            return result;
        }
    }
}

And here is a unit test for it, this uses the HtmlHelperFactory class I described here:

using System.Linq;
using System.Web.Routing;
using KableDirect.Web.FrontEnd.Helpers;
using MbUnit.Framework;

namespace KableDirect.UnitTests
{
    [TestFixture]
    public class TestHtmlEncode
    {
        [Test]
        public void test_all_chars_are_encoded()
        {
            var html = HtmlHelperFactory.CreateInstance(new RouteData());
            var specials = "–—‘’‚“”„†‡•…‰€™ŒœŠšŸƒ’ÀàÁáÂâÃãÄäÅåĀāĂ㥹ǞǟǺǻÆæǼǽḂḃĆćÇçČčĈĉĊċḐḑĎďḊḋĐđÐðDZDzdzDŽDždžÈèÉéĚěÊêËëĒēĔĕĘęĖėƷʒǮǯḞḟƒfffiflffifflſtǴǵĢģǦǧĜĝĞğĠġǤǥĤĥĦħÌìÍíÎîĨĩÏïĪīĬĭĮįİıIJijĴĵḰḱĶķǨǩĸĹĺĻļĽľĿŀŁłLJLjljṀṁŃńŅņŇňÑñʼnŊŋNJNjnjÒòÓóÔôÕõÖöŌōŎŏØøŐőǾǿŒœṖṗŔŕŖŗŘřɼŚśŞşŠšŜŝṠṡſßŢţŤťṪṫŦŧÞþÙùÚúÛûŨũÜüŮůŪūŬŭŲųŰűẀẁẂẃŴŵẄẅỲỳÝýŶŷŸÿŹźŽžŻż";
            var encoded = html.EncodeFull(specials);
            var specialChars = specials.ToCharArray();
            var missedChars = encoded.ToCharArray().Intersect(specialChars);
            string output = "";
            foreach(var ch in missedChars)
            {
                output += "{\"" + ch + "\", \"&#" + ((int) ch) + ";\"},\n";
            }
            Assert.IsEmpty(output);
        }
    }
}
 

Use CacheProfile attribute with output caching in asp.net mvc November 22, 2009

Filed under: Asp.Net,C#,MVC,Web — Rupert Bates @ 9:21 pm

I’ve recently been setting up some caching on a new Asp.Net MVC site by using the OutputCache attribute on my controllers:


//cache this for an hour
 [OutputCache(Duration=60 * 60, VaryByParam="")]

This works really well except that it means hard codeding the cache time into my app so if I want to change it I need to recompile and deploy my code which is obviously far from ideal. So I changed my code to load the value from the web.config and ran into this error:

‘An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type’

Not very helpful.

But fortunately there is a better way of doing all this using cache profiles, I just set up a cache profile in my web.config:


<system.web>
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="HomePage" duration="3600" varyByParam="None" location="ServerAndClient"/>
</outputCacheProfiles>
</outputCacheSettings>

</caching>

</system.web>

And then reference this in my attribute:

 [OutputCache( CacheProfile="HomePage")]

And bingo, we’re sucking diesel as a friend of mine says!

 

Create an Asp.Net MVC HtmlHelper for use in unit tests November 17, 2009

Filed under: Asp.Net,C#,Coding,MVC,Web — Rupert Bates @ 5:47 pm

Here’s a utility factory class to create an HtmlHelper instance so that you can unit test extension methods written on it. It will take a controller and a model for methods which rely on those. I adapted it from this post, and changed it to work with Asp.Net version 1.0 and to accept a model.

using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using KableDirect.Web.FrontEnd.Controllers;

namespace KableDirect.UnitTests
{
    class HtmlHelperFactory
    {
        public static HtmlHelper CreateInstance(RouteData route)
        {
            return CreateInstance(route, new HomeController());
        }
        public static HtmlHelper CreateInstance(RouteData route, Controller controller)
        {
            return CreateInstance(route, controller, null);
        }
        public static HtmlHelper CreateInstance(RouteData route, Controller controller, object model)
        {
            HttpContextBase httpContext = new HttpContextDummy();

            var cc = new ControllerContext(httpContext, route, controller);
            var vd = new ViewDataDictionary(model);
            ViewContext vc = new ViewContext(cc, new ViewDummy(), vd, new TempDataDictionary());
            return new HtmlHelper(vc, new ViewDataContainerDummy(vd), new RouteCollection());
        }


        // Dummy classes needed to be able to create HtmlHelper

        private class HttpRequestDummy : HttpRequestBase
        {
            public override string ApplicationPath
            {
                get { return ""; }
            }

            public override string AppRelativeCurrentExecutionFilePath
            {
                // Any shorter string here gives exception:
                // index larger than length of string
                get { return "~/"; }
            }

            public override string PathInfo
            {
                get { return ""; }
            }
        }

        private class HttpResponseDummy : HttpResponseBase
        {
            public override string ApplyAppPathModifier(string virtualPath)
            {
                return virtualPath;
            }
        }

        private class HttpContextDummy : HttpContextBase
        {
            public override HttpRequestBase Request
            {
                get { return new HttpRequestDummy(); }
            }

            public override HttpResponseBase Response
            {
                get { return new HttpResponseDummy(); }
            }
        }

        private class ViewDummy : IView
        {
            public void Render(ViewContext viewContext, System.IO.TextWriter writer)
            {
                throw new NotImplementedException();
            }
        }

        private class ViewDataContainerDummy : IViewDataContainer
        {
            public ViewDataContainerDummy()
            {
            }

            public ViewDataContainerDummy(ViewDataDictionary dataDictionary)
            {
                _data = dataDictionary;
            }

            private ViewDataDictionary _data;
            public ViewDataDictionary ViewData
            {
                get { return _data; }
                set { _data = value; }
            }
        }
    }
}

 

 

Access RouteData for the current Route from a library class in asp.net mvc October 7, 2009

Filed under: Asp.Net,C#,MVC — Rupert Bates @ 1:20 pm

Sometimes it’s useful to be able to access the current RouteData from outside a controller, for instance in a library class. In webforms you could always do


HttpContext.Current

but in Asp.Net MVC it’s a bit less obvious how you get access to this data.

I’ve found the following code works:


RouteTable.Routes.GetRouteData(new HttpContextWrapper(HttpContext.Current));

Whether there is an easier way or not I don’t know, but this will do me for now…

 

Enumeration model binder for asp.net mvc August 8, 2009

Filed under: C#,MVC,Uncategorized — Rupert Bates @ 9:06 pm

We were recently working on an asp.net MVC app and I noticed that in a lot of places actions were taking strings as parameters and then converting them to enum values with the action. The reason for doing this rather than just typing the parameter as the enum type was because they were optional and so when the value didn’t exist an error was thrown:

screenshot of error page

screenshot of error page

Because enums are not nullable types they can’t be used as optional parameters.

What was needed then was a way to provide a default parameter for when the parameter didn’t exist in the url. To do this I wrote a custom model binder and it solved the problem pretty well.

Here is the code for the binder

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace EnumTest.Helpers
{
public class EnumBinder<T> : IModelBinder
{
private T DefaultValue { get; set; }
public EnumBinder(T defaultValue)
{
DefaultValue = defaultValue;
}

#region IModelBinder Members
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
return bindingContext.ValueProvider[bindingContext.ModelName] == null
? DefaultValue
: GetEnumValue(DefaultValue, bindingContext.ValueProvider[bindingContext.ModelName].AttemptedValue);
}

#endregion

public static T GetEnumValue<T>(T defaultValue, string value)
{
T enumType = defaultValue;

if ((!String.IsNullOrEmpty(value)) && (Contains(typeof(T), value)))
enumType = (T)Enum.Parse(typeof(T), value, true);

return enumType;
}
public static bool Contains(Type enumType, string value)
{
return Enum.GetNames(enumType).Contains(value, StringComparer.OrdinalIgnoreCase);
}
}
}

And to use it in your project you just register it in your Global.asax, specifying the type of your enum and the default value you want when there is no value available in the input.

protected void Application_Start()
        {
            RegisterRoutes(RouteTable.Routes);
            ModelBinders.Binders.Add(typeof(TestEnum), new EnumBinder<TestEnum>(TestEnum.rupert));
        }