Wednesday, June 15, 2011

Improving website performance

I was watching a very interesting video from MIX11 which deals with optimizing website performance using ASP.NET. A lot of the tips are general to web development, and not just ASP.NET, so I think it's worth watching for anyone. If you don't feel like watching it, then at least just check out the below:

  1. Yahoo's YSlow Plugin

  2. Google's Page Speed Plugin

  3. Best Practices for Speeding Up Your Web Site, from Yahoo

  4. Page Speed Family, from Google (look for the 'Page Speed rules' menu)

At the time that I was looking into these plugins, Page Speed wasn't available for Firefox 4 (I believe it is now?) so I didn't try it out, but they basically both grade your website and give you tips on how to improve it - for example, if you don't have compression turned on, if you don't minify your JavaScript, or if your number of requests is high. Along with what is wrong and short tips on how to improve, you also get links to articles on the web explaining the concept. Take YSlow for example: if you haven't setup ETags you can click the 'Read More' link, which takes you to Yahoo's 'Best Practices for Speeding Up Your Web Site' page, to the 'Configure ETags' section.

So try them today, and don't worry about getting 100%, but try get to at least 90% - even small things like ensuring GZIP compression is turned on will make a noticeable difference.

There is also a mod_pagespeed Apache module from Google Page Speed which can do automatic optimizations, I haven't tried that, nor would I really be confident in trying it just because I'm a control freak, but it is from Google so it probably just works. If anyone has used it successfully, please let me know.

Thursday, June 9, 2011

Starting Windows Services Remotely

Did you know you can use the sc command to start services remotely?

sc \\SERVERNAME start SERVICENAME

e.g.

sc \\test-server query AppFabricCachingService
sc \\test-server start AppFabricCachingService

Tuesday, May 10, 2011

Visual Studio Clipboard Ring

Today I found out about the Visual Studio Clipboard Ring, which keeps the last 15 entries that you have entered into the clipboard using copy and cut (CTRL+C, CTRL+V, SHIFT+DEL), and lets you paste into the Visual Studio Editor an item from that list.

You just press CTRL+SHIFT+V when pasting, and each successive press will cycle through the entries. Very useful if you kept something on the clipboard, but then pressed SHIFT+DEL to delete a line, forgetting about what you wanted to paste! Which I tend to do a lot!

Friday, May 6, 2011

iPad

I have an iPad 2 :)

In fact I am busy typing this entry out on it. So far so good. I think I'll be blogging a bit more now that I'm also connected via 3G. Luckily the signal seems to be quite decent and speedy. The Wordpress app isn't the greatest at the moment though, I would definitely enjoy a WYSIWYG editor and a confirmation before uploading my entry. But it ain't that bad for a free app :)

Another much needed feature is the ability the pinch zoom the editor view, especially when correcting text.

Update: 2012-02-01: Switching back to Blogger from Wordpress :)

Wednesday, March 2, 2011

Time Tracking - Grindstone

If you have ever wondered how much time you spend on tasks, then try out the free tool called Grindstone, from the folks at Epiforge.

It has great features, and an easy to use, intuitive interface. You can:

  • Create tasks on the fly

  • Track time against a task, or add time manually

  • Create multiple profiles for tracking various tasks

  • Move time entries between tasks (in case you forgot to switch tasks while tracking)

  • Move tasks between profiles

  • Delete time entries

  • Export to XML

Plus more.

One nice optional feature, if anyone uses the Pomodoro Technique, is receiving a reminder every so many minutes to take a break. Another optional reminder for the workaholics, aptly called the 'Work-a-holic Deterrent', is one which reminds you that you've been working more than a set amount of hours and minutes.

One thing that bugs me is by default the icons 'flash' on each tick when timing a task (the play button and the icon in the Windows system tray), but it has settings to turn this off. That's what I love about it most, you're not tied down to using it the way they intended, but can configure quite a lot to fit your work flow.

Start using it now!

If there's any other time tracking tools you've found and like, post them in a comment (with a link). I don't think there's any out there now though that are as feature rich and intuitive as Grindstone.

Wednesday, February 16, 2011

Xml and XPath queries in .NET with C#

Querying Xml should be easy using XPath. But for some reason I had an issue where for the life of me I was sure the XPath should have been returning something, but was returning nothing. The issue turned out to be because I wasn't specifying namespaces.

The below Unit Tests illustrate the ways that work and don't work (you will need a reference to System.Xml in the project).

Note: I must update this to use a nice code formatter, used Wordpress before, think Blogger doesn't have it out the box.


using System.Xml;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace XmlTestProject
{
    [TestClass]
    public class XmlTests
    {
        /// 
        /// A test to see if we can query using XPath correctly when there is no namespace defined.
        /// This test passes.
        /// 
        [TestMethod]
        [TestCategory("Unit")]
        public void CanQueryWhenNoNamespace()
        {
            string xml = @"Jamesj@miebarrow.comjames@work.co.za";

            string emailsXpath = "//email";

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);

            XmlNodeList emails = doc.SelectNodes(emailsXpath);

            Assert.AreEqual(2, emails.Count, "There should be exactly two nodes retrieved");
            XmlNode emailNode = emails.Item(0);
            Assert.AreEqual("email", emailNode.Name, "The first node should be an email element");
            Assert.AreEqual("j@miebarrow.com", emailNode.InnerText, "The inner text of the email element should be the email address");
        }

        /// 
        /// A test to see if we can query using XPath correctly when there is a namespace defined,
        /// using the same method as when no namespace is defined.
        /// This test fails.
        /// 
        [TestMethod]
        public void CanQueryWhenDefaultNamespace()
        {
            string xml = @"Jamesj@miebarrow.comjames@work.co.za";

            string emailsXpath = "//email";

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);

            XmlNodeList emails = doc.SelectNodes(emailsXpath);

            Assert.AreEqual(2, emails.Count, "There should be exactly two nodes retrieved");
            XmlNode emailNode = emails.Item(0);
            Assert.AreEqual("email", emailNode.Name, "The first node should be an email element");
            Assert.AreEqual("j@miebarrow.com", emailNode.InnerText, "The inner text of the email element should be the email address");
        }

        /// 
        /// A test to see if we can query using XPath correctly when there is a namespace defined,
        /// by providing the namespaces to the SelectNodes function, and explicitly using the
        /// namepsace prefix in our XPath expression.
        /// This test passes, and is the correct way to achieve our goal.
        /// 
        [TestMethod]
        public void CanQueryWhenDefaultNamespaceAndUsingNamespaceManager()
        {
            string xml = @"Jamesj@miebarrow.comjames@work.co.za";

            string emailsXpath = "//jb:email";

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);

            XmlNamespaceManager namespaceManager = new XmlNamespaceManager(doc.NameTable);
            namespaceManager.AddNamespace("jb", "http://jamiebarrow.com/2011/02/16");

            XmlNodeList emails = doc.SelectNodes(emailsXpath, namespaceManager);

            Assert.AreEqual(2, emails.Count, "There should be exactly two nodes retrieved");
            XmlNode emailNode = emails.Item(0);
            Assert.AreEqual("email", emailNode.Name, "The first node should be an email element");
            Assert.AreEqual("j@miebarrow.com", emailNode.InnerText, "The inner text of the email element should be the email address");
        }

        /// 
        /// A test to see what happens when we specify a default namespace, and also a different
        /// namespace, and perform a query using the non-default namespace.
        /// This test passes.
        /// Note that the element we retrieve is fully qualified with a namespace prefix, whereas
        /// in previous examples, the elements were part of the default namespace and had were
        /// not qualified with a prefix.
        /// 
        [TestMethod]
        public void CanQueryWhenDefaultNamespaceAndUsingNamespaceManagerWithMixedNamespaces()
        {
            string xml = @"Jamesj@miebarrow.comjames@work.co.zaanother@example.com";

            string emailsXpath = "//test:email";

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);

            XmlNamespaceManager namespaceManager = new XmlNamespaceManager(doc.NameTable);
            namespaceManager.AddNamespace("jb", "http://jamiebarrow.com/2011/02/16");
            namespaceManager.AddNamespace("test", "http://test.org/");

            XmlNodeList emails = doc.SelectNodes(emailsXpath, namespaceManager);

            Assert.AreEqual(1, emails.Count, "There should be exactly two nodes retrieved");
            XmlNode emailNode = emails.Item(0);
            Assert.AreEqual("test:email", emailNode.Name, "The first node should be an email element, qualified by a namespace prefix since we are querying the non-default namespace");
            Assert.AreEqual("another@example.com", emailNode.InnerText, "The inner text of the email element should be the email address");
        }
    }
}