nik codes

PerfMatters.Flush Goes 1.0!

Pluralsight If you like my blog, you’ll love my Pluralsight courses:
Tracking Real World Web Performance
WebPageTest Deep Dive

In my previous two performance related posts I’ve gone on and on about the benefits of flushing an HTTP response early and how to do it in ASP.NET MVC. If you haven’t read those yet, I recommend you take a quick moment to at least read Flushing in ASP.NET MVC, and if you have a little extra time go through More Flushing in ASP.NET MVC as well.

I think those posts did a decent job of explaining why you’d want to flush early. In this post I’m going to dig into the details of how to flush early using my library, PerfMatters.Flush.

Three Usage Patterns

The core of what you need to know to use PerfMatters.Flush is that I’ve tried to make it easy to use by providing a few different usage models. Pick the one that works best in your scenario, and feel free to mix and match across your application.

1. Attribute Based

The easiest way to use PerfMatters.Flush is via the [FlushHead] action filter attribute, like this:

[FlushHead(Title = "Index")]
public ActionResult Index()
      // Do expensive work here
      return View();

The attribute can be used alone for HTML documents with a static <head> section. Optionally, a Title property can be set for specifying a dynamic <title> element, which is very common.

2. Code Based

For more complex scenarios, extension methods are provided which allow you to set ViewData or pass along a view model:

public ActionResult About()
     ViewBag.Title = "Dynamic title generated at " + DateTime.Now.ToLocalTime();
     ViewBag.Description = "A meta tag description";
     ViewBag.DnsPrefetchDomain = ConfigurationManager.AppSettings["cdnDomain"];


     // Do expensive work here
     ViewBag.Message = "Your application description page.";
     return View();

As you can see, this mechanism allows for very dynamic <head> sections. In this example you could imagine a <title> element, <meta name="description" content="…"> attribute (for SEO purposes) and <link rel="dns-prefetch" href="…"> (for performance optimization) all being set.

3. Global Lambda

Finally, PerfMatters.Flush offers a model to flush early across all your application’s action methods – which simply leverages the same global action filters that have been in ASP.NET MVC for years now:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
     filters.Add(new HandleErrorAttribute());
     filters.Add(new FlushHeadAttribute(actionDescriptor =>
         new ViewDataDictionary<CustomObject>(new CustomObject())
             {"Title", "Global"},
             {"Description", "This is the meta description."}

In this case we pass a Func<ActionDescriptor, ViewDataDictionary> to the FlushHeadAttribute constructor. That function is executed for each action method. This example is pretty contrite since the result is deterministic, but you can see both a custom model (CustomObject) and ViewData in use at the same time.

In real world usage the actionDescriptor parameter would be analyzed and leveraged to get data from some data store (hopefully in memory!) or from an IOC container.

Installation & Setup

Getting up and running with PerfMatters.Flush is as easy as installing the NuGet package.

From there, you’ll want to move everything you’d like to flush out of _Layout.cshtml to a new file called _Head.cshtml (which sits in the same directory). Here’s an example of _Head.cshtml:

<!DOCTYPE html>
     <meta charset="utf-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     @if (ViewBag.Description != null)
         <meta name="description" content="@ViewBag.Description">
     <title>@ViewBag.Title - My ASP.NET Application</title>

Here’s its corresponding _Layout.cshtml file:

     <!-- Lots of lovely HTML -->
     <div class="container body-content">
         <hr />
             <p>&copy; @DateTime.Now.Year - My Application</p>
     @RenderSection("scripts", required: false)

Notice the @Html.FlushHead() method on the first line? That’s the magic that stiches this all together. It allows you to use MVC the way your used to on action methods that you don’t want to flush early, and opt-in (via one of the usage model’s described above) when you do.

Wrapping Up

PerfMatters.Flush has been fun for me to work on. It’s currently being used in production on and has nicely boosted perceived performance on the pages that do any database queries.

To be honest, I wish that PerfMatters.Flush didn’t have to exist. I’ve asked the ASP.NET team to look into baking first class support for flushing early into the framework, but I don’t foresee that happening for quite awhile. Either way, there are tons of application built in MVC 3, 4 and 5 that can leverage this now.

The project is open source, and the code is hosted on GitHub. I’d love to hear your feedback on ways to make it better and easier to use.

Single Post Navigation

18 thoughts on “PerfMatters.Flush Goes 1.0!

  1. So vnext isn’t going to have this baked in?

    • nikmd23 on said:

      Unfortunately, nothing has been announced to say that it will be. I’m hopeful, but wouldn’t hold my breath.

  2. Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1636

  3. Pingback: Dew Drop – June 24, 2014 (#1802) | Morning Dew

  4. Pingback: PerfMatters – HTTP Flushing in MVC | Garrett S. Y. Hampton

  5. Ed S on said:

    Great set of posts. When I tried to add this via NuGet, I get the following:
    Install failed. Rolling back…
    Install-Package : Could not install package ‘PerfMatters.Flush 1.0.0’. You are trying to install this package into a project that targets ‘.NETFramework,Version=v4.0’, but the package does not contain any assembly
    references or content files that are compatible with that framework. For more information, contact the package author.At line:1 char:1
    + Install-Package PerfMatters.Flush
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Install-Package], InvalidOperationException
    + FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PowerShell.Commands.InstallPackageCommand

  6. Ed S on said:

    Using a standard HandleErrorAttribute that redirects if an exception occurs, like a DB failure, is no longer possible since you can’t redirect to the error page once the HTTP headers are sent to the client.

    • nikmd23 on said:

      Can you tell me how you are getting that redirect behavior?

      MVC 5 seems to just inject the error view into the response, not redirect, in my tests.

  7. Pingback: PerfMatters.Flush now CourtesyFlush | nik codes

  8. Pingback: NuGet Package of the Week - Courtesy Flush to flush earlier and speed up ASP.NET - Scott Hanselman

  9. Pingback: NuGet Package of the Week - Courtesy Flush to flush buffers earlier and optimize time to first byte - Scott Hanselman

  10. vNext is going to have async flush built in.

    Are there any differences in the implementation? Why would you pick one over the other?

    • nikmd23 on said:

      CourtesyFlush is part of the inspiration for the flushing in vNext. As such, CourtesyFlush will not be updated to support vNext. CourtesyFlush is targeted at the current MS web development stack.

  11. Great package! Is there a way to specify different headers? I would like to have multiple _Layout.cshtml files that all point to different _Header.cshtml files. Is that currently possible?

    • nikmd23 on said:

      It is with the latest release! (Though PerfMatters.Flush is now called CourtesyFlush.) Just add the HeaderName property to your [FlushHead] attribute like this:
      [FlushHead(Title = "Some Title", HeaderName = "_Head2")]

  12. dod294 on said:

    Great package! Is there a way to specify a different _Header.cshtml file ex. _Header2.cshtml? I would like to have multiple _Layout.cshtml files all pointing to different _Header.cshtml files. Is that currently possible?

  13. Pingback: HTTP Flushing in ASP.NET MVC – CourtesyFlush – Sridhar Pasham

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: