Friday, February 5, 2010

Viewing connections to a server

I keep on forgetting how to do this so here it is documented for my convenience.

Start Performance Monitor (PerfMon)

Click + to add new monitor.

Enter computer's name

To view connections for a web service

From Performance object drop down select "Web Service"

From Select counters from list drop down select "Current Connections"

From Select instances from list select "_Total"

To view connections for a web app

From Performance object drop down select "ASP.NET Apps v2.0.50727"

Tuesday, February 2, 2010

Combine, compress, and update your CSS file in ASP.NET MVC

After doing some analysis using Google's web master tools I discovered that I needed to make some improvements to how the CSS file(s) on a busy site were being delivered. This post follows on from and improves on Automatically keeping CSS file current on any web page.

  • The CSS files need to be combined into a single file.
  • It needs to be compressed.
  • It needs to be cached, but only until it's changed.
  • Image references need to be dynamic.

I'm using ASP.NET MVC so the first thing that I did was to add an Action to a Controller that was to be the new CSS file. Instead of referencing a physical file on disk the CSS file has now become a resource that follows this pattern:

~/Site/Css/20100201105959.css

Site is the controller, Css is the action, and 20100201105959.css is the single parameter that this action accepts.

This is what the SiteController class looks like:

[CompressFilter]
public class SiteController : Controller
{
    static ContentResult cr = null;

    [CacheFilter(Duration=9999999)]
    public ActionResult CSS(string fileName)
    {
        try
        {
            if (cr == null)
            {
                StringBuilder sb = new StringBuilder();
                foreach (string cssFile in Constants.cssFiles)
                {
                    string file = Request.PhysicalApplicationPath + cssFile;
                    sb.Append(System.IO.File.ReadAllText(file));
                    sb.Append("!!!!!");
                }

                sb.Replace("[IMAGE_URL]", StaticData.Instance.ImageUrl);

                cr = new ContentResult();
                cr.Content = sb.ToString();
                cr.ContentType = "text/css";
            }

            return cr;
        }
        catch (Exception ex)
        {
            System.Diagnostics.Trace.TraceError(ex.Message);
            return View();
        }
    }
}

Notice that we have a couple of filters on the class and method. The [CompressFilter] on the class will check the browser's capability for gzip or deflate and activate compression for any action/method in this class. The [CacheFilter] will add a 3 month "cache this file" direction to the HTTP header of the HTML that's returned.

The Constants.cssFiles returns a list of root-relative CSS files that need to be combined into the single response.

The Replace() function on the StringBuilder allows us to put a tag in the CSS files so that any references to images can be dynamically determined at runtime. This allows us to run the images off a url such as http://localhost:1234/images/ during development and http://images.mysite.com/ in production. Storing images in a subdomain instead of the main domain will improve performance because more images will be able to be downloaded in parallel by the browser.

The single fileName parameter is a dummy parameter that is needed to make the URL different when one of the underlying CSS files is modified but still allow optimum caching in the browsers. i.e. by changing this value in the HTML we return we force the browser to re-fetch the CSS but only when one of the underlying CSS files change.

The ContentResult is a static variable so that the building of this CSS file is a single hit after the App Pool has been recycled.

The following snippet of code is placed in the code behind page for the Site.Master. Normally you would not have a code behind page in the MVC model but I have not been able to work out how to dynamically inject the CSS file name into the master file any other way.

protected void Page_Load(object sender, EventArgs e)
{
    HtmlLink css = new HtmlLink();
    css.Href = String.Format("/Site/CSS/{0}", StaticData.Instance.CssFileName);
    css.Attributes["rel"] = "stylesheet";
    css.Attributes["type"] = "text/css";
    css.Attributes["media"] = "all";
    Page.Header.Controls.Add(css);
}

The CSS file name is generated with the following snippet of code:

string _CssFileName = null;public string CssFileName
{
    get
    {
        if (String.IsNullOrEmpty(_CssFileName))
        {
            DateTime dt = new DateTime(2000,1,1);
            foreach (string cssFile in Constants.cssFiles)
            {
                string file = System.Web.HttpContext.Current.Server.MapPath(cssFile);
                FileInfo fi = new FileInfo(file);
                DateTime lastWriteTime = fi.LastWriteTime;
                if (lastWriteTime > dt)
                {
                    dt = lastWriteTime;
                }
            }
            _CssFileName = dt.ToString("yyyyMMddHHmmss") + ".css";
        }
        return _CssFileName;
    }
}

We iterate through each of the CSS files and extract the most recent modified date. Using this date, we generate the the CSS file name. That way a different file name will be injected into the HTML whenever one of the CSS files is modified and this will force the browser to reload the new CSS file keeping it always up-to-date.

It may seem like a lot of work for a CSS file at first glance but the benefits are enormous and the added flexibility will allow you to change it on a whim.

I haven't shown you the Constants.cssFiles but that's just an array of file names. I was also thinking of implementing this as a loop that found all the *.css files in a folder. That way if you added a new CSS file it would automatically be included without a code change. However, the disadvantage of this is that you cannot predetermine the order in which the CSS files are combined into the single file and the order is usually important.

If, however, you did want to use that all-css-in-one-folder approach you could adopt a naming convention such as 01_myfile.css, 02_myfile.css and then sort the file names before combining them.

Google to help kill IE6

I got an email from Google this morning which in part said:

"...over the course of 2010, we will be phasing out support for Microsoft Internet Explorer 6.0..."

This is great news. I have a couple of sites that I work on that just don't work in IE6 and it's the bane of my life. I really don't want to be wasting investing time in getting IE6 to work when I could be adding new features and improving performance. 

With Google behind this I am hoping that this will accelerate users upgrading from IE6.

Also related are the IE6 Update and the IE6 No More sites.

Saturday, January 30, 2010

Ultra-Fast ASP.NET

Ultra-Fast ASP.NET

Just started reading Ultra-Fast ASP.NET by Richard Kiessig (Build Ultra-Fast and Ultra-Scalable web sites using ASP.NET and SQL Server).

So far very good. Will post more here.

Richard Kiessig on twitter: http://twitter.com/UltraFastASPNET

 

Saturday, January 23, 2010

Automatically keeping CSS file current on any web page

The problem: You update your site's CSS file but your users aren't seeing your latest crazy colors and styles that you've selected for your web site.

The reason: Your users' browsers have cached the CSS files and it could take days before those caches expire and your new CSS file is requested from your server.

The solution: Change the name of your CSS file that's linked in the header of your page each time you change the contents of the CSS file.

The new problem: You don't want to change the name manually each time because (1) it may need changing in more than one place, (2) you might forget to change it, (3) you're lazy, (4) you might miss somewhere it needs to be changed, (5) each change increases the risk you might do something wrong.

The new solution: Have it done automatically for you.

This is how I implemented it using ASP.NET and C#. I did this on a hybrid ASP.NET MVC and WebForms web site that has two base master pages; one for MVC and one for WebForms. All other master pages inherit from these two master pages so there were just two locations that need to be changed.

In the <head> tag I originally had something like this:

<link href="/site.css" rel="stylesheet" type="text/css" media="all" />

and I wanted something like this:

<link href="/site.css?v=1" rel="stylesheet" type="text/css" media="all" />

with the 1 changing each time the site.css file changed.

In any major project that I'm working on I usually have a class called something like StaticData. This class holds arbitrary bits of data that are loaded or calculated once and then never or rarely change. It's like a hybrid of a constants file and a cache.

In this class I added the following property and private variable:

string _CssVersion = null;
public string CssVersion
{
    get
    {
        if (String.IsNullOrEmpty(_CssVersion))
        {
            try
            {
                string cssFile = System.Web.HttpContext.Current.Server.MapPath("~/site.css");
                FileInfo fi = new FileInfo(cssFile);
                DateTime lastWriteTime = fi.LastWriteTime;
                _CssVersion = lastWriteTime.ToString("yyyyMMddHHmmss");
            }
            catch
            {
                return "1";
            }
        }
        return _CssVersion;
    }
}

So what I'm doing is generating a version number based on the time stamp of the CSS file. If we update the CSS file then that time stamp will automatically change and we'll only have to load it once because after that it's in the "constants cache."

To load the CSS name dynamically in ASP.NET we add the following code snippet to the Page_Load() function of the code behind file of the master page. This applies to both WebForms and MVC applications.

HtmlLink css = new HtmlLink();
css.Href = String.Format("/site.css?v={0}", StaticData.Instance.CssVersion);
css.Attributes["rel"] = "stylesheet";
css.Attributes["type"] = "text/css";
css.Attributes["media"] = "all";
Page.Header.Controls.Add(css);

The Instance property of the StaticData class is a public static property of type StaticData making this class a singleton.

Tuesday, January 19, 2010

Graffiti CMS now open source

This blog runs on Graffiti CMS. I have wined in the past about this not being open source and have on ocassion thought about moving it to an open source blog engine. Today I read that Graffiti CMS is now open source. I have never done a blow by blow comparison of .NET blog engines so I don't know if Graffiti is the best but I can say that it has worked very smoothly for me and has some clever built in features which I like.

This is a great contribution to the open source community by Telligent - thank you.

Source code is available here: http://graffiticms.codeplex.com/

Monday, January 18, 2010

jQuery drag and drop tree plugin

I've been researching a jQuery drag and drop tree plugin for a project that I'm working on and so far I've found the following:

1. jsTree

So far this is my favorite. It does almost everything that I want including in place editing of the tree items. One drawback is that it itself requires a ton of plugins to work and is complex to setup and this makes it brittle in my opinion. The creator, however, is frantically working on a new release which I think will simplify things and dramatically improve this already excellent plugin. I've decided that this is the one that I'll probably use but will wait until the next major release comes out.

Fantastic set of demo pages - this is what "sells" plugins - if you don't have a create demo page then you will dramatically reduce your chances of getting users to use your plugin.

2. SimpleTree

Good but does not support in place editing. Big plus that it has a demo page but the demo is fairly simple and I suspect that it has more features that have not been demo'd.

3. Drag Drop Tree

The demo looks reasonable but no work has been done on this (so it seems) since 2007 and there's not much other documentation about this plugin.