blogs.conchango.com

welcome to the conchango blogging site
Welcome to blogs.conchango.com Sign in | Join | Help
in Search

Anthony Steele's Blog

  • User interface usablity with spoken numbers

    Just a quick note on something that was on my mind.

    I knew someone who lived on a "Seven Sisters road", at number 20. She knew that "Seven Sisters" was a really bad name for a road. Just picture yourself phoning for a taxi at "Number 20, Seven Sisters road". Say it three times quickly if you're not hearing the issue.

    Also "One" was a bad name for a train company - Imagine anouncements like "The 12:20 One service to Basingstoke is on time on platform three" People who arrive on platform 3 at 12:20:15 are going to be sorely disapointed.

    Is there a moral for techies in this? Maybe. But it is a user interface design issue in the broader sense, and shows how user interfaces can mislead people, impair thier performance and cause frustration in ways that the original designer did not anticipate. Often "getting it right" just means not falling into any of a large number of potential pitfalls. Some of which are not obvious.

  • REST from WCF 3.5

    WFC is well known to be brilliant at SOAP, and fans of WCF may be aware that in version 3.5 may be aware of the cool new features. But the rest of us (the REST of us, haha, I'm so funny)  may not know that it addresses in a simple and flexible way other scenarios- that is, serving XML or JSON data off simple Urls in a REST style.

     

    This note is be based information from talks by Aaron Skonnard (http://www.pluralsight.com/blogs/aaron/) and Richard Blewett (http://www.dotnetconsult.co.uk/weblog2/) given at Devweek. I went to these talks to get a basic understanding of WCF, and found a couple of interesting features as well.

     

    Before the REST in introduced , I'll start with a simple service that when you give it your name, will greet you.

     

    The contract is:

     

    using System.ServiceModel;

     

    namespace GreeterLib

    {

        [ServiceContract]

        public interface IGreeterService

        {

            [OperationContract]

            string Greet(string name);

        }

    }

     

    The service implementation is

     

    namespace GreeterLib

    { 

        public class GreeterService : IGreeterService

        {

            public string Greet(string name)

            {

                return string.Format("Hello, {0}", name);

            }

        }

    }

     

    This is hosted for debug purposes in a console as follows:

     

    using System;

    using System.ServiceModel;

     

    using GreeterLib;

     

    namespace GreeterConsole

    {

        class Program

        {

            static void Main(string[] args)

            {

                using (ServiceHost serviceHost = new ServiceHost(typeof(GreeterService)))

                {

                    serviceHost.Open();

     

                    Console.WriteLine("WCF Service is running...");

                    Console.ReadLine();

     

                    serviceHost.Close();

                }

     

                Console.WriteLine("WCF Service has closed");

            }

        }

    }

     

    And as always with WFC, the config file does a lot of the heavy lifting of tying things together:

      

    <?xml version="1.0" encoding="utf-8" ?>

    <configuration>

      <system.serviceModel>

        <bindings>

         

        </bindings>

        <services>

          <service name ="GreeterLib.GreeterService" behaviorConfiguration="Default">

            <host>

              <baseAddresses>

                <add baseAddress="http://localhost:8080/greeter"/>

              </baseAddresses>

            </host>

            <endpoint

              address=""

              binding ="basicHttpBinding"

              contract="GreeterLib.IGreeterService" />

          </service>

        </services>

        <behaviors>

          <serviceBehaviors>

            <behavior name="Default">

              <serviceMetadata httpGetEnabled="true"/>

            </behavior>

          </serviceBehaviors>

        </behaviors>

      </system.serviceModel>

    </configuration>

     

     

    So far so usual and SOAPy. I can run the console, and verify that at http://localhost:8080/greeter there is a service, it has wsdl at http://localhost:8080/greeter?wsdl, which specifies the soap that I would need to craft if I wanted to talk to it. I'm not going to do that manually.

     

    To do this in a REST style, we change as follows:

    The contract needs a WebGet attribute, specifying the URL to match:

     

    using System.ServiceModel;

    using System.ServiceModel.Web;

     

    namespace GreeterLib

    {

        [ServiceContract]

        public interface IGreeterService

        {

            [OperationContract]

            [WebGet(UriTemplate = "greet/{name}")]

            string Greet(string name);

        }

    }

     

     

    The program needs to use WebServiceHost:

     

    using System;

    using System.ServiceModel;

    using System.ServiceModel.Web;

     

    using GreeterLib;

     

    namespace GreeterConsole

    {

        class Program

        {

            static void Main(string[] args)

            {

                using (WebServiceHost serviceHost = new WebServiceHost(typeof(GreeterService)))

                {

                    serviceHost.Open();

     

                    Console.WriteLine("WCF Service is running...");

                    Console.ReadLine();

     

                    serviceHost.Close();

                }

     

                Console.WriteLine("WCF Service has closed");

            }

        }

    }

     

    The config needs to use the webHttpBinding binding

     

          <service name ="GreeterLib.GreeterService" behaviorConfiguration="Default">

            <host>

              <baseAddresses>

                <add baseAddress="http://localhost:8080/greeter"/>

              </baseAddresses>

            </host>

            <endpoint

              address=""

              binding ="webHttpBinding"

              contract="GreeterLib.IGreeterService" />

          </service>

     

    And there you go. You can now run this, and hit the URL http://localhost:8080/greeter/greet/Anthony

     

    and get the response

    <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">Hello, anthony</string>

     

    You can also return data in JSON format by setting attributes.

     

    But if you hack around a bit, you can also take full control of the XML returned, and serve some Plain Old XML if you want, as follows:

     

    The contract must return a Message:

     

    using System.Runtime.Serialization;

    using System.ServiceModel;

    using System.ServiceModel.Channels;

    using System.ServiceModel.Web;

     

    namespace GreeterLib

    {

        [ServiceContract]

        public interface IGreeterService

        {

            [OperationContract]

            [WebGet(UriTemplate = "greet/{name}")]

            Message Greet(string name);

        }

    }

     

    The service:

     

    using System.ServiceModel.Channels;

    using System.Xml;

    using System.Text;

    using System.IO;

     

    namespace GreeterLib

    {

     

        public class GreeterService : IGreeterService

        {

            public Message Greet(string name)

            {

                XmlReader xmlReader = MakeXml(name);

                return Message.CreateMessage(MessageVersion.None, "", xmlReader);

            }

     

            private XmlReader MakeXml(string name)

            {

                StringBuilder buffer = new StringBuilder();

                using (XmlWriter xmlWriter = XmlWriter.Create(buffer))

                {

                    xmlWriter.WriteStartDocument();

                    xmlWriter.WriteStartElement("greeting");

                    xmlWriter.WriteAttributeString("type", "hello");

                    xmlWriter.WriteString(string.Format("Hello, {0}", name));

                    xmlWriter.WriteEndElement();

                    xmlWriter.WriteEndDocument();

                }

     

                TextReader reader = new StringReader(buffer.ToString());

                return XmlReader.Create(reader);

            }

        }

    }

     

    And you'll ret the returned data:

     

    <greeting type="hello">Hello, anthony</greeting>

     

    So there it is: POX over REST served by WCF. And it wasn't very difficult.

  • LINQ as a DSL

    What kind of thing is Language Integrated Query, exactly? You can draw the line in a number of places, but I am going to cut it down to the core, where LINQ is a Domain Specific Language.

     

    There are LINQ-enabling language features, i.e the language extensions of type inference:

                var s = "This is a string";

     

    Anonymous types and instance initialiser blocks:

                var anObject = new { x = 3, y = 4.5, z = true };

     

    Lambdas:

                var someStrings = new List<string>();

                var longStrings = someStrings.Where(item => item.Length > 5);

     

    Also extension methods, and the c# code in System.Linq.

     

    So, once all of this is factored out, what is left of LINQ? It is just a compiler rule (or rules) for turning

                var longStringsLinq =

                    from sItem in longStrings

                    where sItem.Length > 5

                    select sItem;

     into

                var longStringsLinq = someStrings.Where(item => item.Length > 5).Select(sItem => sItem);

     

    Whereafter compilation carries on as before. The transformation rule is a Domain-Specific Language which is baked into the c# Version 3 compiler.

     

    We have the option of using the other enabling features for our own purposes. But the LINQ DSL is not accessible, unlike in some languages such as Ruby, in which DSLs are user accessible. I still prefer reading c# code to Ruby code, but I'm thinking - what would c# look like if we could define our own DSLs in a relatively easy way? What would you do with it? Would chaos or enlightenment result?

     

    (this post was inspired by Neal Ford's talk at devweek on "“Design patterns” in dynamic languages")

  • Devweek - first two days

    Monday was a wet and windy day. It was a day of all-day workshops, and I elected to get  all the WPF done at once. Dave Wheeler gave an excellent presentation.

    Interesting  that desktop is far more entrenched than Silverlight, for a couple of reasons: firstly it has been out and stable for longer, and secondly that it has an niche (advanced or scalable desktop Windows user interfaces) for which there is no competitor, whereas Silverlight's is competing with flash and a lot of flavours of AJAX.

    I came out with a much better overview of WCF. And more desire to use it.

    Tuesday

    Tuesday was a sunny day, and the conference had moved to the bottom levels of the Barbican. And grown much larger, with far more people, and vendor booths. The barbican is  wonderful, huge labyrinth. This is the first year that the conference is in this larger venue. I got a few pieces of swag.

    Excluding the staff, the gender ratio must have been about 50 male to 1 female.

    David Wheeler gave the keynote speech, on Rich Internet Applications. 

    New tools since last DevWeek:
    VS 2008 AJAX
    Silverlight 1

    On the way now:
    ASP.NET – MVC, Data services
    WPF Enhancements
    Entity framework

    We are looking at Software in the cloud
    Social networking -> social change

    Rich applications are driven by media and conversations.

    And we have to monetise the web -  it has to pay for itself, by search placement and ads.

    What is a RIA?
    - data driven
    - secure
    - good performance
    - Runs connected
    - Engaging

    Trade off Reach vs. richness. 
    HTML has the widest reach, least richness,


    AJAX can be used for RIA with wider reach
    Silverlight for RIA with slightly less reach, more richness

    WinForms for richer, less reach. Not a RIA anymore.
    WPF-> most richness, least reach.

    The compelling thing about Silverlight is not flash developers using Silverlight, it is C# developers using Silverlight. There are a lot more C# developers. They can use existing skills. Unlike flash, can interact with the dev.

    NBC Olympics will be a big stress test of SilverLight.

    Then I was on on to track 2, in Cinema 3, Floor 4 for 10 ways to improve your code, Neal Ford, Thoughtworks. It was a good session, mostly ideas not code, but not dry. Ask me about the angry moneys some time. I should print out a "YAGNI" sign some time.

    Daniel Moth gave us a tour of VS2008, and Alex Homer gave a session on the Enterprise Application block. It's not the most glamourous code, but it works very well. We use it, maybe should use it more. Version 4 is apparently out soon, and will include a Dependency Injection block.

  • DevWeek (and DeveloperDay)

    Conchango is considerately (though unexpectedly) sending me to DevWeek (http://www.devweek.com/) next week. I'll try to give feedback on the event and on sessions that I attend. Looking at the agenda, the top topics seem to be WPF in desktop and Silverlight flavours, ASP.Net with AJAX, WCF, patterns and testing.

    I've never been to a devweek before, and I'm quite pleased to go. But I have been to several DeveloperDay (http://www.developerday.co.uk/) events, I like them. The information density is high, so maybe by the end of a whole week like that my brain will be reeling. 

    DeveloperDay is a great community event: it’s free for attendees, presenters are not paid, and the topics presented are driven by the developer community with calls for presentation and agenda voting. The venue and facilities (but not the presenters) are provided for free by Microsoft in Reading. But in some ways DeveloperDay is a victim of its own success – the venue has a finite capacity, and the event is fully booked very rapidly, with many of the places going to the same keen people each time.

    Increasing the capacity of DeveloperDay would probably mean rethinking the whole thing –  using a different, larger venue would not be free, so  the attendees would have to pay for it, so the speakers would have to be chosen as drawcards, etc. That sounds more like devweek, and the community feeling of DeveloperDay is fine like it is.  Developer day is instead branching out with regional events like http://www.developerdayscotland.com/ and http://www.dddireland.com/

    Devweek looks interesting, and I'll try to report back; but with five tracks it's probable that I will miss whichever session you really wanted to hear about. I'm already torn between several of them.

  • Duplicate Finder

    A while ago (June last year) I wrote a utility to detect runs of duplicate lines in files, which is useful for looking for repetitive code that should be refactored. Then I stopped work on it, since it was done.  The original blog post is here and the project is up on CodePlex here.

    This year I have revisited it with two new features which I think make it much more usable.

    The first feature  is an MSBuild Task wrapper to compliment the command line interface. This means that instead of using a command line like: 

    DuplicateFinder -r -t8 -eAssemblyInfo *.cs

    You can now also use an equivalent MSBuild Script:

    <Project DefaultTargets="RunTest"
        xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
       
      <UsingTask TaskName="DuplicateFinder"
     AssemblyFile="$(MSBuildExtensionsPath)\DuplicateFinder.Tasks.dll" />
     
      <ItemGroup>
        <TestFiles
       Include="..\..\**\*.cs" 
       Exclude="..\..\**\AssemblyInfo.cs" 
     />
      </ItemGroup>
     
      <Target Name="RunTest">
        <DuplicateFinder Files="@(TestFiles)"
            DuplicateThreshold="8"
      />
      </Target>
    </Project>

    This may be more verbose, but it has the advantage that you can run it as part of your automated build process. In case you don't always look at the build output, you can use the CountThresholdForError and LengthThresholdForError options to the MSBuild task to fail the build if the duplicates in the source are too numerous or too long.

    The second new feature is to cut down or eliminate the false positives. Whole files can already be excluded, but we also need to exclude duplicates where the first line starts with a particular prefix. I'll show you the reason for this.

    If we run the duplicate finder on its own source code (excluding the generated AssemblyInfo.cs files, which we know will all look much the same), we get the following:

    > DupFinder.exe -r -t7 -eAssemblyInfo.cs *.cs
     
    Processing in C:\Temp\DuplicateFinder\DuplicateFinderLib
    6 files read
    Duplicate of length 7 at:
     Line 1-7 in C:\Temp\DuplicateFinder\DuplicateFinderLib\DuplicateEventArgs.cs
     Line 1-7 in C:\Temp\DuplicateFinder\DuplicateFinderLib\LineItem.cs
     Line 1-7 in C:\Temp\DuplicateFinder\DuplicateFinderLib\LineItemList.cs
    1 duplicate found

    The duplicate, line 1-7 of three different files, consists of these lines:

    using System;
    using System.Collections.Generic;
    using System.Text;
     
    namespace DuplicateFinderLib
    {
        /// <summary>

    While these lines may be the same, I don't regard them as "bad" or "cut and paste" code. So I exclude duplicates where the first line starts with "using". Like so:


    DupFinder.exe -r -t7 -eAssemblyInfo.cs -xusing *.cs
    Processing in C:\Temp\DuplicateFinder\DuplicateFinderLib
    6 files read
    0 duplicates found

    You can also do this in the MSBuild file. More than one prefix can be excluded.

  • New Microsoft certifications

    I'm back from Holiday and getting going into the new year. For those of you .Net developers considering Microsoft certification exams, the following may be of interest:

    Exam 70-502 Microsoft .NET Framework 3.5 - Windows Presentation Foundation. Expected to be released in February 2008. http://www.microsoft.com/learning/exams/70-502.mspx

    Exam 70-503 Microsoft .NET Framework 3.5 - Windows Communication Foundation. Expected to be released in February 2008. http://www.microsoft.com/learning/exams/70-503.mspx

    Exam 70-504 Microsoft .NET Framework 3.5 - Windows Workflow Foundation. Expected to be released in March 2008.  http://www.microsoft.com/learning/exams/70-504.mspx

  • Sandcastle part 3

    I found more things to do with sandcastle after part 1 and part 2. Here's a trick that made the sandcastle build simpler. The basic problem is that sandcastle must run after all the binaries are built by projects in a solution (so that it can read those binaries), but before the WiX installer is built (so that the sandcastle output can be packaged by WiX).

    WiX V3 can be a project in the solution, but Sandcastle is unfortunately not. Life would be easier if it was, and I hope that some day it will be, but for now we must make do. And we can make do with a dummy project. The procedure is as follows:

    Create an empty project called "MyProject.Documentation" in your solution. In the project dependencies, make sure that it depends on all the projects that build binaries that will be documented, and that the Wix installer depends on it. So now it's in the right place in the build order, between the binaries and the WiX

    Now fire up your text editor and edit the MyProject.Documentation.csproj file. It's an MSBuild script, which means that previous tricks can be applied, i.e. create or replace the build target with the following line:

      <Target Name="Build" DependsOnTargets="BuildSandcastleDocs">

      </Target>

    So now the build step has been rejigged to skip compilation of the (non-existent) source files of this project, and instead build the sandcastle. The BuildSandcastleDocs is similar to in the last few posts, but not identical:

    Above that the following definitions:

      <PropertyGroup>

          <SHFBConsolePath>C:\Program Files\EWSoftware\Sandcastle Help File Builder\SandcastleBuilderConsole.exe</SHFBConsolePath>

      </PropertyGroup>

      <ItemGroup>

         <SandcastleProjects Include="$(MSBuildProjectDirectory)\*.shfb" />

      

      <SandSources Include="

    $(MSBuildProjectDirectory)\..\MyProject.UI\Bin\$(Configuration)\*.exe;

    $(MSBuildProjectDirectory)\..\ MyProject.UI\Bin\$(Configuration)\*.dll; $(MSBuildProjectDirectory)\..\ MyProject.UI \Bin\$(Configuration)\*.xml" 

          />

      </ItemGroup>

      <Target Name="BuildSandcastleDocs">

          <!-- Build source code docs -->

          <Message Text="Copying @(SandSources) to $(OutDir)" />

          <Copy SourceFiles="@(SandSources)" DestinationFolder="$(OutDir)" />

          <Message Text="Building projects for sandcastle:@(SandcastleProjects)" />

          <!-- Update the output path -->

          <Message Text="Replacing OutDir with $(OutDir)" />

          <Attrib Files="@(SandcastleProjects)" ReadOnly="false"/>

          <FileUpdate Files="@(SandcastleProjects)" Regex="\$\(OutDir\)" ReplacementText="$(OutDir)" />

          <Exec Command="&quot;$(SHFBConsolePath)&quot; &quot;@(SandcastleProjects)&quot; -outputpath=&quot;$(OutDir).&quot;" />

      </Target>

    The main change from last time is SandSources, a group of files of type *.exe, *.dll and *.xml which are copied to the Sandcastle project's output dir.  This is done to make it work on a dev machine as well as on a build server. On the build server, all projects share the same $(OutDir), but on a dev  machine all projects use different ones.

    On a local machine it copies binary files from the last project's output dir to the Sandcastle project's output dir.  On a build server this doesn't need to be done. However this step does nothing on the build server, since no output files are located in those folders. So the build works both ways now.

    Actually we have two versions of the solution – one with the Sandcastle and wix projects for CI use; and a faster one without for constant desktop use.

  • Further Sandcastle integration

    Further to my last post on sandcastle in TFS builds, I wanted to update on the complications that have happened in the last week. 

    Sandcastle docs and Wix installers.

    Our installer, built in WiX v3, acts as a project inside the solution and builds last, rolling up the output of the other projects into an MSI file. The sandcastle action ran after this. Now we wanted to put the sandcastle .chm file into the installer, which couldn't happen as things were. The answer to this was to put the installer into its own solution, and run them in this order:

          <Target Name="Build"

          DependsOnTargets="GenerateBuildVersion;BuildSolutions;BuildSandcastleDocs;BuildWiXInstaller">

          </Target>

    Sandcastle.targets file

    I'm starting to appreciate breaking down build files into smaller, single-purpose, potentially reusable chunks. We now have a file called sandcastle.targets that contains the sandcastle targets and definitions.

    Release builds

    The astute among you will have noticed that the contents of the Project.Documentation.shfb that I gave last time will work only for debug builds: paths such as <assembly assemblyPath="..\..\..\..\..\..\Binaries\Debug\Assembly1.dll" will get the wrong files, or be entirely missing during release builds. There is still no good way to paramaterise the .shfb file, but you can hack it a bit.


    We are now doing these paths like this in the shfb file:

      <assemblies>

        <assembly assemblyPath="$(OutDir)Assembly1.dll " xmlCommentsPath="$(OutDir)Assembly1.XML"

           commentsOnly="False" />

    Then we use  the MSBuild FileUpdate task to replace the text "$(OutDir)" with the contents of the variable of the same name. Before that, the Attrib task is needed to make the file writable.

    Both of these are tasks are found in the MSBuild Community Tasks (http://msbuildtasks.tigris.org/)

     So here's the complete sandcastle.targets file.