Thursday, September 21, 2006

How to examine code and write a class with EnvDTE

Further to my experiments with the Guidance Automation Toolkit, I've been playing with generating code with my custom guidance package. Looking at the Service Factory GAT that's been released by the Patterns and Practices group, they use three different techniques for code generation; T4 templates, EnvDTE and CodeDom. If they use all three, I wondered which one I should be using. I've previously used the CodeDom in other projects and although it's very powerfull, you use it to generate the syntactic structure of the code and can then generate C#, VB or whatever, it is really long winded. T4 templates are at the opposite end of the spectrum, a bit like asp for code generation, you simply write a template of the code you want to generate and put code between <# #> marks that the template engine runs. The problem with it at the moment is that they are really new and the tools are there yet. There's no intellisense or code coloring for it and debugging isn't easy either.

So I decided to have a look at the EnvDTE Visual Studio automation class library for code generation. A lot of the GAT stuff seems to be built around it, so it's a natural fit for code generation duties. Unfortunately the documentation isn't that great, and this little demo of how to navigate a code file and write a class took much longer than it should have. But here it is, It gets the current visual studio environment and enumerates though all the projects and project items. It then examines itself outputting all the code elements and finally writes a new class inside its own namespace. If you try this out, make sure you name the file it's in 'HowToUseCodeModelSpike.cs'.

using System;
using NUnit.Framework;
using EnvDTE;
using EnvDTE80;

namespace Mike.Tests
{
    [TestFixture]
    public class DteSpike
    {
        [Test]
        public void HowToUseCodeModelSpike()
        {
            // get the DTE reference...
            DTE2 dte2 = (EnvDTE80.DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.8.0");

            // get the solution
            Solution solution = dte2.Solution;
            Console.WriteLine(solution.FullName);

            // get all the projects
            foreach(Project project in solution.Projects)
            {
                Console.WriteLine("\t{0}", project.FullName);

                // get all the items in each project
                foreach(ProjectItem item in project.ProjectItems)
                {
                    Console.WriteLine("\t\t{0}", item.Name);

                    // find this file and examine it
                    if(item.Name == "HowToUseCodeModelSpike.cs")
                    {
                        ExamineItem(item);
                    }
                }
            }
        }

        // examine an item
        private void ExamineItem(ProjectItem item)
        {
            FileCodeModel2 model = (FileCodeModel2)item.FileCodeModel;
            foreach(CodeElement codeElement in model.CodeElements)
            {
                ExamineCodeElement(codeElement, 3);
            }
        }

        // recursively examine code elements
        private void ExamineCodeElement(CodeElement codeElement, int tabs)
        {
            tabs++;
            try
            {
                Console.WriteLine(new string('\t', tabs) + "{0} {1}", 
                    codeElement.Name, codeElement.Kind.ToString());

                // if this is a namespace, add a class to it.
                if(codeElement.Kind == vsCMElement.vsCMElementNamespace)
                {
                    AddClassToNamespace((CodeNamespace)codeElement);
                }

                foreach(CodeElement childElement in codeElement.Children)
                {
                    ExamineCodeElement(childElement, tabs);
                }
            }
            catch
            {
                Console.WriteLine(new string('\t', tabs) + "codeElement without name: {0}", codeElement.Kind.ToString());
            }
        }

        // add a class to the given namespace
        private void AddClassToNamespace(CodeNamespace ns)
        {
            // add a class
            CodeClass2 chess = (CodeClass2)ns.AddClass("Chess", -1, null, null, vsCMAccess.vsCMAccessPublic);
            
            // add a function with a parameter and a comment
            CodeFunction2 move = (CodeFunction2)chess.AddFunction("Move", vsCMFunction.vsCMFunctionFunction, "int", -1, vsCMAccess.vsCMAccessPublic, null);
            move.AddParameter("IsOK", "bool", -1);
            move.Comment = "This is the move function";

            // add some text to the body of the function
            EditPoint2 editPoint = (EditPoint2)move.GetStartPoint(vsCMPart.vsCMPartBody).CreateEditPoint();
            editPoint.Indent(null, 0);
            editPoint.Insert("int a = 1;");
            editPoint.InsertNewLine(1);
            editPoint.Indent(null, 3);
            editPoint.Insert("int b = 3;");
            editPoint.InsertNewLine(2);
            editPoint.Indent(null, 3);
            editPoint.Insert("return a + b; //");
        }
    }
}

Thursday, September 14, 2006

Are you a nerd?

I tried this are you a nerd test today. My result was 'Mid Level Nerd'. I probably saved by the fact that I'm a family man and my wife kinda provides me with a social life :)

Wednesday, September 13, 2006

How to do database source control and builds

Most business applications are fundamentally database applications. A business application's database is as much a part of the application as its C# source code. Unfortunately, in many development shops versioning and controlling the database is done very differently from the in memory source code. For example:

  • Often a single 'development' database is shared between the developers. This means that if a developer makes a schema change he runs the risk of breaking his colleague's environments at least until the next build. Often this leads to a fear of changing the database schema.
  • The database is often versioned independently of the in memory source code. This makes is hard to deploy a specific build because often the database backups and the in memory source are are not syncronised.
  • The database is often not source controlled, or if it is, it is done as a single build mega-script. This makes it impossible to package linked database and in memory source changes as a single 'feature' or 'fix'. It makes it hard or impossible to roll out a single failing feature. Also, because the mega-script is often created by choosing the 'script database' feature of the dbms it is checked into source control under the build manger's login or whatever login the build scripts are running under. This means that it is impossible to track database changes. I can't go to a single table's create script in source safe, look for the last time it was checked in and find out who made the change and then read the comments to found out why it was changed. The best I can mange is to look through every single version of a huge script file looking for a change, but then there's no way of knowing who made it or why it was made.

How should you manage your database development then? Well the best thing is to treat it as much as you can just like all your other source code:

  • Every developer should develop against their own database instance.
  • A database backup should be stored with the executables from the build. That database backup should be a backup of a database built from the sql scripts retrieved from source control at the same label as all the other source.
  • The database should be maintained as object scripts. Each table, stored procedure, view and function should have its own script. To make any change in their local database a developer should check out the object they want to change, apply that to their database, unit test the change and then check in the change as a package with any other source for that feature or fix. The developer should label the package and comment it with a link to a feature or defect number.
  • Before a developer starts work on the next feature or fix he should get the source from the last build label (which, with continuous integration should also be the latest version) and restore his local database from the same labelled backup.

In order to do this you need tools that allow you to do the following:

  • Build a database from object level source files. Most dbms will choke if you try to just run the scripts in without first working out a build order by examining the object references. The tool must automate this for you, it's far to onerous to try to do this manually.
  • Be able to upgrade a database to a new schema version by calculating 'alter' scripts.

Here's where I plug a product that a couple of guys I know and worked with at NTL have developed, DbGhost. It allows you to adopt all those good practices that I've listed above. I've got it adopted on several projects now and they still haven't given me any commission or offered me a lucrative consultancy contract:(

Tuesday, September 12, 2006

Windows Live Writer

I've been using Windows Live Writer to write this post and the last one. In fact the last post was something I'd already written for our internal wiki and I was able to cut and paste it from there straight into the WLW window. It worked! It's a really cool tool and makes blogging a lot easier.

How to create a guidance package

After creating my first test guidance package using the Guidance Automation Toolkit (GAT), I've cobbled together some bullet point steps on how to do it. This is really rough at the moment, but I'll be adding to it as my knowledge grows. It took a while because there's nothing similar to it, like a walkthrough, and I didn't like using the meta guidance package because I wanted to understand how it all hung together first.

Creating the initial solution

  • Create a new solution with a class library project.
  • Add References:
EnvDTE
EnvDTE80
Microsoft.Practices.Common
Microsoft.Practices.ComponentModel
Microsoft.Practices.RecipeFramework
Microsoft.Practices.RecipeFramework.Common
Microsoft.Practices.RecipeFramework.Library
Microsoft.Practices.RecipeFramework.VisualStudio
Microsoft.Practices.WizardFramework
System
System.Windows.Forms
  • Tools->Guidance Package Manager->Enable/Disable Packages->Choose Guidance Package Development
  • Create a new class library project, name it Installer
  • Add References
Microsoft.Practices.RecipeFramework
Microsoft.Practices.RecipeFramework.VisualStudio
Microsoft.VisualStudio.TemplateWizardInterface
System
System.Cofiguration.Install
  • Set a project dependence of your guidance package project on the installer project
  • Create a new class called 'InstallerClass' in the installer project.
using Microsoft.Practices.RecipeFramework; 
namespace TestGuidancePackageInstaller
{
    /// <summary>
    /// Installer class for the guidance package
    /// </summary>
    [System.ComponentModel.ToolboxItem(false)]
    public class InstallerClass : ManifestInstaller
    {
    }
}
  • Create the guidance package xml configuration document, MyGuidancePackageName.xml, see documentation for details
<?xml version="1.0" encoding="utf-8" ?>
<GuidancePackage xmlns="http://schemas.microsoft.com/pag/gax-core"
Name="GuidancePackageName"
Caption="My Guidance Package"
Description="A test guidance package"
BindingRecipe="BindingRecipe"
Guid="fdd8f06f-6d6d-4228-96db-f842076764af"
SchemaVersion="1.0">
<Overview Url="Docs\Overview.htm"/>
<Recipes>
<Recipe Name="BindingRecipe">
<Types>
<TypeAlias Name="RefCreator" Type="Microsoft.Practices.RecipeFramework.Library.Actions.CreateUnboundReferenceAction, Microsoft.Practices.RecipeFramework.Library"/>
</Types>
<Caption>Creates unbound references to the guidance package</Caption>
</Recipe>
</Recipes>
</GuidancePackage>
  • Set the properties of MyGuidancePackageName.xml to BuildAction="Content", Copy to Output Directory="Copy if newer"
  • Build the solution
  • On the solution context menu select 'Register Guidance Package'
  • Open a new instance of VS, create a new project, go to Tools->Guidance Package Manager->Enable/Disable Packages
  • Should see you package

Create the Binding Recipe

  • Add a new Recipe element under Recipes
  • Set its name to 'BindingRecipe'
  • Add attribute BindingRecipe="BindingRecipe" to the GuidancePackage root element
  • Add types:
<Types>
<TypeAlias Name="RefCreator" Type="Microsoft.Practices.RecipeFramework.Library.Actions.CreateUnboundReferenceAction, Microsoft.Practices.RecipeFramework.Library"/>
</Types>
  • For each recipe you want to reference, add Actions:
<Action Name="<name of action>" Type="RefCreator" AssetName="<name of recipe to bind>" ReferenceType="<unboundRecipeReference class>" />

Create a recipe

  • Add a new Recipe element under Recipes
  • Set attribtes Name="its name" Bound="false"
  • Add Caption
  • Add HostData, this adds the recipe to the Solution, Project or Item context menus
<HostData>
<Icon ID="<icon number>"/>
<CommandBar="Project"/>
  • Add Arguments to specify the arguments that this recipe requires
  • Add GatheringServiceData to define the wizard that gets the arguments
  • Add Actions to specify what the recipe should do.

How to create and execute a T4 template

  • Create a folder 'Templates' in the guidance package project
  • Create a folder 'Text' in the 'Templates' folder
  • Add a template file with the extension .t4
  • Set the properties of the .t4 file: 'Build Action = Content', 'Copy to output directory = Copy if newer'
  • Write your .t4 template (see documentation on this)
  • Create a new Recipe as above
  • Create Arguments for all the properties of the .t4 template
  • Add an argument for the TargetFileName that adds .cs to the class name argument
<Argument Name="TargetFileName">
<ValueProvider Type="Microsoft.Practices.RecipeFramework.Library.ValueProviders.ExpressionEvaluatorValueProvider, Microsoft.Practices.RecipeFramework.Library" 
Expression="$(ClassName).cs">
<MonitorArgument Name="ClassName"/>
</ValueProvider>
</Argument>
  • Add an argument for the currently selected project
<Argument 
Name="CurrentProject" 
Type="EnvDTE.Project, EnvDTE, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<ValueProvider Type="Microsoft.Practices.RecipeFramework.Library.ValueProviders.FirstSelectedProject, Microsoft.Practices.RecipeFramework.Library" />            
</Argument>
  • Add an action to execute the template:
<Action Name="<action name>" 
Type="Microsoft.Practices.RecipeFramework.VisualStudio.Library.Templates.TextTemplateAction, Microsoft.Practices.RecipeFramework.VisualStudio.Library"
Template="Text<name of template>.t4">
<Input Name="<name of template property>" RecipeArgument="<recipe argument name>"/>
… as many input elements as you have properties
<Output Name="Content"/>
</Action>
  • Add an action to write a file to the currently selected project:
<Action Name="<actio name>" Type="Microsoft.Practices.RecipeFramework.Library.Actions.AddItemFromStringAction, Microsoft.Practices.RecipeFramework.Library" Open="true">
<Input Name="Content" ActionOutput="GenerateHelloClassAction.Content" />
<Input Name="TargetFileName" RecipeArgument="TargetFileName" />
<Input Name="Project" RecipeArgument="CurrentProject" />
</Action>

How to use solution and project templates to create a new solution structure from the New->Project menu in VS

  • Add a project folder, 'Templates', to the Guidance Package project.
  • Add a sub folder to 'Templates' called 'Solutions'.
  • Add a file called Solution.vstemplate to the 'Solutions' folder
  • Add an icon named Solution.ico to the 'Solutions' folder
  • Write the Solution.vstemplate. This is a 'multi-project' vstemplate with additions for GAT, example below:
<VSTemplate
Version="2.0"
Type="ProjectGroup"
xmlns="http://schemas.microsoft.com/developer/vstemplate/2005">
<TemplateData>
<Name>Test Guidance Package</Name>
<Description>A guidance package created to learn how to create guidance packages</Description>
<ProjectType>CSharp</ProjectType>
<Icon>Solution.ico</Icon>
<CreateNewFolder>false</CreateNewFolder>
<DefaultName>GatTest</DefaultName>
<ProvideDefaultName>true</ProvideDefaultName>
</TemplateData>
<TemplateContent>
<ProjectCollection>
<ProjectTemplateLink ProjectName="$ProjectName$">Projects\Domain\Domain.vstemplate</ProjectTemplateLink>
</ProjectCollection>
</TemplateContent>
<WizardExtension>
<Assembly>Microsoft.Practices.RecipeFramework.VisualStudio, Version=1.0.60429.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Assembly>
<FullClassName>Microsoft.Practices.RecipeFramework.VisualStudio.Templates.UnfoldTemplate</FullClassName>
</WizardExtension>
<WizardData>
<Template xmlns=http://schemas.microsoft.com/pag/gax-template
SchemaVersion="1.0"
Recipe="CreateSolution">
<References>
</References>
</Template>
</WizardData>
</VSTemplate>
  • Note the Recipe="CreateSolution" attribute of the template element under WizardData. This should point to a recipe defined in the MyGuidancePackage.xml file. This recipe is executed when the solution loads so you can use it to gather information from the user and execute any actions to build the solution items.
  • Under the 'Solutions' folder, create a folder called 'Projects'
  • Under the 'Projects' folder, create a folder for each project. Give it the project name
  • Add a ProjectName.vstemplate file to the project folder. Here's an example:
<VSTemplate
Version="2.0"
Type="Project"
xmlns="http://schemas.microsoft.com/developer/vstemplate/2005">
<TemplateData>
<Name>Domain model</Name>
<Description>A domain model for the application</Description>
<Icon>Domain.ico</Icon>
<ProjectType>CSharp</ProjectType>
<CreateNewFolder>false</CreateNewFolder>
<DefaultName>Domain</DefaultName>
<ProvideDefaultName>true</ProvideDefaultName>
</TemplateData>
<TemplateContent>
<Project File="Domain.csproj" ReplaceParameters="true">
<ProjectItem ReplaceParameters="true">Properties\AssemblyInfo.cs</ProjectItem>
</Project>
</TemplateContent>
<WizardExtension>
<Assembly>Microsoft.Practices.RecipeFramework.VisualStudio, Version=1.0.60429.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Assembly>
<FullClassName>Microsoft.Practices.RecipeFramework.VisualStudio.Templates.UnfoldTemplate</FullClassName>
</WizardExtension>
<WizardData>
<Template xmlns=http://schemas.microsoft.com/pag/gax-template
SchemaVersion="1.0">
<References>
</References>
</Template>
</WizardData>
</VSTemplate>
  • Add a ProjectName.csproj file. This is a standard project template. Here's an example, but you can take any existring .csproj file as a template (just insert the appropriate $variables$ at the right place) 

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>$guid1$</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>$safeprojectname$</RootNamespace>
<AssemblyName>$safeprojectname$</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System"/>
<Reference Include="System.Data"/>
<Reference Include="System.Xml"/>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSHARP.Targets" />
</Project>

  • Add a ProjectName.ico icon file
  • Set the properties of all the items added above to Build Action = "Content", Copy to output directory = "Copy if newer"
  • Add a new folder 'Properties' under the project folder
  • Add an AssemblyInfo.cs file under the Properties folder
  • Insert the appropriate $variables$ to replace the values that visual studio automatically provides. Here's an example:
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("$projectname$")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("$registeredorganization$")]
[assembly: AssemblyProduct("projectname")]
[assembly: AssemblyCopyright("Copyright © $registeredorganization$ $year$")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("$guid1$")]

// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

  • Set the properties of the AssemblyInfo.cs file to Build Action="Content", Copy to output directory = "Copy if newer"
  • Build the solution and Register the guidance package
  • Open a new instance of Visual Studio, select File->New->Project, Your Guidance Automation Package should now appear under 'Guidance Packages'.

Adding documentation to your guidance package

  • Create a new solution folder called 'Docs'.
  • Add a new HTML page called 'Overview.htm'
  • Set the properties of the Overview.htm file to Build Action="Content", Copy to output directory = "Copy if newer"
  • Add the following <Overview> element to your MyGuidancePackage.xml file under the document element:
<Overview Url="Docs\Overview.htm"/>
  • When you choose your guidance package, the Overview.htm page will display in the guidance navigator window 

Thursday, September 07, 2006

Fustrating Visual Studio Templates

VS 2005 has a new template feature that allows you to easily create item and project templates that you can add to the 'new item' and 'new project' dialogue boxes. It's pretty cool, you just select an item or a project from an existing solution and click file->Export template, a wizard pops up that allows you to name the template and optionally deploy it. Scott Guthrie has a nice post about it on his excellent blog (a required read for .net developers). As well as projects and items there's also a multi-project template that you're supposed to be able to use to produce template solutions. I've been playing with this for last couple of days in order to create a web service solution template for our team and it's pretty disapointing. Not much thought seems to have gone into it and there are two things in particular that make it useless for me. First of all, there's no way of customising the name of the projects in the solution. In the new solution dialogue you can choose the name of your solution and most .net projects I've worked on also use this as a solution namespace. Usually you name the projects in the solution prefixed with the solution name which plays well with the default namespace behaviour in visual studio. For example, say I call my solution 'Mike.MegaThing'. I'd then name my projects things like, 'Mike.MegaThing.Domain', 'Mike.MegaThing.Service', 'Mike.MegaThing.Dal'. Now every time I create a new class in 'Mike.MegaThing.Domain', the namespace is automatically created as 'Mike.MegaThing.Domain', perfect. Unfortunately the multi-project-template has no way of allowing you to change the name of it's projects. Although template items, like classes, can be tokenised and expanded with a range of default parameters like $safeprojectname$ this doesn't apply to the .vstemplate files themselves. My solution template has to have fixed project names like 'Service' or 'Domain' with similar default namespaces, which is crap because I want 'Mike.MegaThing.Dal' to be in a different namespace to 'Mike.LittleThing.Dal'. The other thing which makes the multi-project template useless is the wierd way it creates the folder structure for your created solution. Normally, if you add a project to a solution it creates a sub folder under your solution folder with the project name and puts the .csproj file in there. The multi-project template doesn't do this, instead it creates a sub folder under the solution folder with the name of the solution and then creates the project folders in that. There seems to be no way of altering this behaviour. So, thumbs down to visual studio templates for creating template solutions, although if you just want simple project or item templates it's perfectly adequate. I'm now going to be digging into the Guidance and Automation Toolkit (GAT) which looks much closer to what I need, more soon.

Software Factories, Domain Specific Languages and related stuff

I've recently had to get up to speed on some new buzzwords: 'Domain Specific Language' (DSL), 'Software Factories' and the 'Guidance and Automation Toolkit' (GAT). What do they all mean, and how do they relate to each other? I found it quite hard to find out. Two of them are Microsoft specific; 'Software Factories' and GAT, the other (DSL) is a generic software term, but is also being used for a specific Microsoft product. Martin Fowler explains it here extremely well. Before I get down to the terms themselves, first a little background... There's a constant trend in software development for common tasks to be automated. That's what computers are for. Of course the automation is a very tricky thing to get right and takes a while to evolve. First assemblers automated the conversion of human readable codes to ones and zeros, then compilers automated common tasks in a way that allowed code to be written at a higher level of abstraction. At the same time operating systems and now frameworks gradually handled more and more housekeeping tasks such as opening and closing files, network communication and printing stuff. There were a few false dawns, usually when the level of abstraction is raised too quickly without understanding all the implications, the case tools of the 80's and 90's are a good example. They never really succeeded because they couldn't bridge the gap between code generation and customisation. But now we're on the cusp of a new breakthrough in the level of abstraction we use to build common application types. The nirvana of the business developer is to be able to concentrate on capturing the business entities, relationships and rules and then to press a magic button that creates the application according to current best practices. We're getting closer and closer to this these days. The .net framework provides a huge level of abstraction for common tasks, the Enterprise Application Blocks make it even easier, and it's common to use either code generators such as code smith, or object/relational mappers such as NHibernate to automate data access. If you know all the tools out there and how to use them you can almost reach that nirvana. You can imagine a kind of 'automation of automation' where you might choose say an 'Enterprise smart client application' package that would know how to use all these frameworks and code generators to give you such a magic button. That in short is what Software Factories are. It's Microsoft's word for a collection of code generators and frameworks as well as a mechanism to capture the developer's intentions, that allows the automatic generation of all non business specific code. So the top level abstraction in this set of concepts is the Software Factory. At the practical, actually available today in beta level are the GAT and DSL Tools. I was quite confused about how these played together when I first started looking at them. They seem to do similar things, but which one to use when? In fact they are products of two separate teams at Microsoft who weren't really aware of what the other was doing until they'd progressed some way down their separate roads. The GAT was developed by the Patterns and Practices group; the people who make the Enterprise Application Blocks and produce advice on application architecture. It basically builds on Visual Studio Templates, adding a code templating system similar to code smith and some nice extension points that allow you to create wizards and actions. They've already released a number of software factories based on the GAT, such as the 'Service Factory'. The GAT is the easy-to-use, every software house should have one, way of building software factories. The DSL tools come as part of the Visual Studio SDK, produced by the Visual Studio team. The VS SDK is aimed at people who want to produce extensions for Visual Studio and is a bit more hard core than the GAT. I haven't looked at DSL tools in anywhere near the same depth, but I understand that it's essentially the diagramming engine used by the class designer and various other Visual Studio tools linked to a code generation engine. Apparently both the Visual Studio team and the Patterns and Practices team were working on their own code templating engines and it was only when someone noticed the duplicated effort that the two teams were brought together. The result was the T4 engine that used in both tools. These two toolsets represent quite an exciting development by Microsoft and as I said above, there's an expectation that we're on the cusp of a significant step change in software development. You only have to read about some of the huge productivity benefits of things like Ruby on Rails to appreciate how easy life can be with the right tools.

Friday, August 18, 2006

Reflecting Generics

I had an interesting problem today, how do you find out if an object is a subclass of a generic type? If you've simply got an instance of a generic type...
List<int> myobject = new List<int>();
You can find that myobject is a List<> by using the method GetGenericTypeDefinition():
if(myobject.GetType().GetGenericTypeDefinition() == typeof(List<>)) { ... }
But say you've got a type that inherits from a generic type:
public class A : List<int> { }
Now if you've got an instance of A:
A myA = new A();
You can't call GetGenericTypeDefinition(), it complains that myA.GetType() is in an incorrect state. Similarly, asking this:
myA.GetType().IsSubclassOf(typeof(List<>))
returns false because myA is a subclass of List<int>, but not List<>. I couldn't find any way of doing this without manually walking up the class hierarchy. In the end I gave in and wrote a this little recursive function:
public bool IsDerivedFromGenericType(Type givenType, Type genericType)
{
    Type baseType = givenType.BaseType;
    if (baseType == null) return false;
    if (baseType.IsGenericType)
    {
        if (baseType.GetGenericTypeDefinition() == genericType) return true;
    }
    return IsDerivedFromGenericType(baseType, genericType);
}
I guess where I was going wrong was thinking that List<int> 'inherits' from List<>, but of course it doesn't. It's an entirely unrelated hierarchy. Generics are not inheritance. I spent some time digging through the newsgroups looking for an answer to this and there's quite a lot of confusion around this issue. A really common misconception is to think that, for example, List<int> inherits from List<object> and that you can downcast like this..
List<int> myIntList = new List<int>();
List<object> myObjectList = (List<object>)myIntList;
It doesn't work.

Tuesday, August 15, 2006

A nice soap test tool: SoapUI

Do you manually code test harnesses for your web services? It's a real pain, and because every web service is defined by its wsdl you shouldn't have to do it. I've been using a soap test tool called SoapUI for the last few weeks. It makes experimenting with raw web service requests a breeze. You just have to give it a wsdl url and it will construct an example request which you can fill in with some test values. Then it's just a question of waiting for the response. It saves all the different requests you make in a project file and you can change the endpoint to try out different deployments. It's also got facilities to do load testing and generate statistics for web services, but I haven't had a need to use that yet. The only downside about this tool is that it's got a nasty javaesque interface, cross platform UIs always suck, but this one is not too bad.

Friday, August 11, 2006

Functional Programming

I've just read a really good blog on functional programming by defmacro.org (I couldn't find his/her name on the blog anywhere). I didn't know that Alan Turing, Kurt Godel and John von Neumann all worked together at Princeton. I guess it makes sense that those three guys would know each other, but just imagine being able to listen in to their conversations. Hmm, probably completely over my head:) Anyway, there was a fourth guy, who I'd not heard of before, Alonzo Church. He invented an alternative to the Turing machine called lambda calculus. Of course the Turing machine went on to be the basis of pretty much all modern computers, but Alonzo wasn't entirely forgotten. In the late 50's LISP was invented as a programming language that implemented lambda calculus and is still with us. It's never really been mainstream, but the Emacs text editor is written in LISP, for example. One think that I've read in several places about LISP is that it makes some things, like creating domain specific languages much easier. So why is functional programming interesting? And what can you do that you can't do with imperative programming languages like the C* family? Well, it all goes back to the lambda calculus. I don't want to repeat defmacro's excellent article, but you can do all kinds of cool stuff, like mathematically redictable unit testing, simple parallel processing with no chance of race conditions, and even hot deployment where you can drop in code changes to a running program. Also because of the nature of functional programs (they are, in effect, a single function) you can use the lambda calculus to do automated optimisations to your program or do lazy execution. Also, because a functional language's state is always represented by its stack, unlike an imperative language where the state is a combination of the stack and the heap, you can use 'continuations' to halt and restart your program, or even pass your program's state to a separate program (it's also what allows for hot deployment). Now that C# is begining to get some funcional programming constructs (lambda expressions, expression trees, delayed execution), functional programming is going to become a mainstream concern for dot net developers. Joel Spolsky, has a nice post, Can your programming language do this? It shows some stuff that becomes much easier once functions become first class citizens, and there's a post about it on the powershell blog, showing some of the powerfull functional features of that language. If you look at everyone's current favorite language, Ruby, it already does all this stuff, as well as providing a full compliment of object oriented features. Unfortunately, some of the really cool stuff I mentioned above, like parallelisation or automated optimisations, can only be done with 'pure' functional language like Haskell or Erlang. It's making me think that I'd like to dig into a good Haskell book sometime.