nik codes

PerfMatters.Flush Goes 1.0!

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"];

     this.FlushHead();

     // 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>
<html>
<head>
     <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>
     @Styles.Render("~/Content/css")
     @Scripts.Render("~/bundles/modernizr")
</head>

Here’s its corresponding _Layout.cshtml file:

@Html.FlushHead()
<body>
     <!-- Lots of lovely HTML -->
     <div class="container body-content">
         @RenderBody()
         <hr />
         <footer>
             <p>&copy; @DateTime.Now.Year - My Application</p>
         </footer>
     </div>
     @Scripts.Render("~/bundles/jquery")
     @Scripts.Render("~/bundles/bootstrap")
     @RenderSection("scripts", required: false)
</body>
</html>

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 getGlimpse.com 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.

About these ads

Single Post Navigation

14 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.

    https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNet.Mvc.Razor/RazorPage.cs#L537

    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.

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Google+ photo

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

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: