Categories

Migrating Projects to TFS 2010

I am so disappointed with TFS 2010. Ok, the new functions are really cool but NO tooling for migrating existing projects. Microsoft – what were you thinking? I suppose that it would be a little task for a TFS engineer to create a migration tool. Now we have to fiddle with it.

Here is the deal: You created a project on TFS 2008 with let’s say the Agile 4.2 template, then you migrate your server to TFS 2010 and you will notice that the migrated project still use the Agile 4.2 process template and SharePoint site, more or less. There is no automated way to migrate to the Agile 5.0 process template. You will find a couple of blogs describing how to upgrade certain parts for the TFS 2010 beta version. Which is good for inspiration but does not fully work on our TFS 2010 RTM. The best source I could find so far is the Visual Studio 2010 upgrade project on codeplex.

I will update this post along with going through the painful process of manually upgrade my Agile 4.2 project. I just wish that I had waited a little longer and created the project from scratch with the new Agile 5.0 process template on TFS 2010.

Here are my upgrade steps:

  1. Download the Agile 5.0 process template
    Right-click the root node in team explorer/process template/choose Agile 5.0 and select download (you must be TFS project collection administrator to do so)
  2. Manually upload the six work item templates (*.wit) files to my team project with TFS powertools, you can also use witadmin.exe for that purpose
    You might have noticed that some of the work item template have the same name as the old ones such as Bug or Task. These have undergone a major face lift and it would be a shame not to upgrade those. Especially support for link management to parent and child elements as well as an estimate field (Task template) are very nice. But be aware that you will loose all information that was stored in custom fields you added to the old work item template. In that case you should export the current values to an Excel spreadsheet so that you can copy it back into TFS after you updated the work item template.
  3. Iteration planning workbook
    I simply followed the description from the codeplex document and it fully work. The iteration planning workbook is a great help to create user stories and/or spread these over iterations.
  4. Product planning workbook
    The description from the codeplex document worked partially. The most interesting feature of this workbook where you can see the distribution of products does not work for me. I get a meaningless error when choosing the tab.
  5. Test management
    If you want to create test suites and test cases you must use the new Test Management tool that comes with TFS 2010. For the tool to work you must upload the new categories and link types to TFS using the witadmin tool. You can find a detailed walk-through in blog post that is referred to in the codeplex upgrade project mentioned above.

Mont Blanc du Tacul (4248 m)

Saturday. August 29th 2009. Roping up with Alex and Anh at the Refuge les Cosmiques (3613 m), 6.00 a.m. The sun is about to set, clear sky and we are heading out in the dark. We descend easily down to the Col du Midi (3532 m), pass it and start ascending to the Épaule du Mont Blanc du Tacul (4060 m). The route is quite steep, in beginning going straight and than zig-zagging through the ceracs (overhanging blocks of ice) and across some crevasses.

Arriving on the shoulder the weather is beautiful but windy. We meet a party of climbers returning from their ascent of Mont Blanc. They gave up due to the windy conditions. We continue up the shoulder – nice and easy uphill. Shortly before the ridge to the summit we meet two climbers that did not want to do summit in these windy conditions. We continue and meet two British climbers that just finished rappelling from the summit. On the summit we enjoy the sun and almost no wind protected by the surrounding rocks. We take some pictures of us with the Mont Blanc in the background before happily descending along the same route.

P8280878 
Descending from Aiguille du Midi.

P8280882
Col du Midi and Épaule du Mont Blanc du Tacul. You can see the path on the glacier.

P8290897
On the way up looking back to Aiguille du Midi.

P8290908
On the summit of Mont Blanc du Tacul

Run MSTests without Visual Studio 2008

The Microsoft unit testing framework comes with the MSTest.exe to run tests from command-line. This only works on machines that have Visual Studio installed. With some tweaks you can make it run on a computer without Visual Studio (see here). But that requires to add some keys to the registry and copying a bunch of assemblies which might not be an option for a pre-production or production environment.

So I decided to write a little command-line application that runs all tests in an assembly. You can download it from codeplex. It basically does the following:

  1. Load assembly with tests
  2. Load configuration file for assembly
  3. Find all classes marked with the TestClass attribute
  4. Find all methods marked with the TestMethod attribute and execute them

A failing assert statement will throw an exception which the test runner application handles. So counting the passed and failed tests is trivial such as measuring the execution time with a Stopwatch.

Native Boot Windows Server 2008 R2 from VHD

As I developer I am interested to squeeze the most performance out of my development environment. I was so relieved when Windows 7 was introduced supporting booting from VHD files. So instead from running my development environment from a VHD file via Virtual PC 2007 I can boot the VHD directly.

I am using Windows 7 as ‘host’ operating system and followed these steps to install the 2008 R2 on a VHD:

  1. Create a VHD file in disk manager, initialize and format it
  2. Download the Windows AIK from http://www.microsoft.com/downloads/details.aspx?FamilyID=C7D4BC6D-15F3-4284-9123-679830D629F2&displaylang=en
  3. Follow the instructions here to install 2008 R2 on the VHD and add it to the Windows 7 boot menu: http://code.msdn.microsoft.com/InstallWindowsImage/Release/ProjectReleases.aspx?ReleaseId=2662

The whole procedure takes about 15 minutes. Than you can boot the 2008 R2 and install you applications. Pretty cool.

Validating Email Addresses

Ok, validating an email address should not be difficult. You look up the definition in the RFC and write a little regular expression. Here is what I could extract from RFC 822:

address        =  mailbox                      ; one addressee
                 /  group                        ; named list
mailbox        =  addr-spec                    ; simple address
                 /  phrase route-addr            ; name & addr-spec
domain         =  sub-domain *("." sub-domain)
sub-domain     =   domain-ref / domain-literal
domain-literal =  "[" *(dtext / quoted-pair) "]"
domain-ref     =  atom                         ; symbolic reference
atom           =  1*<any CHAR except specials, SPACE and CTLs>
dtext          =  <any CHAR excluding "[",     ; => may be folded
                     "]", "\" & CR, & including
                     linear-white-space>
quoted-pair    =  "\" CHAR                     ; may quote any char

Right, the symbols used are a bit strange: / – or, 1* – one or more, * – zero or more. That clears up things a little. I intend to check for simple addresses only. I do not care about named lists. Then I was a bit shocked about the sub-domain *(“.” sub-domain) expression. I always thought that a domain suffix such as .com or .de would be some what more confined than a sub domain. But ok, this makes things easier and I ended-up with this simple expression:

\w[-.\w]*@[-\w]+(?:\.[-\w]+)+

Attaching User to a Login (SQL Server)

Probably most people already know – but it still happens often to me: When restoring a SQL database you cannot login even though the database user matches the SQL Server login. This can easily be fixed with the following command:

sp_change_users_login ‘AUTO_FIX’, ‘ [User_Name]’

DataContext.Connection.ConnectionString – No Password

My favourite way to clean up database tables is with a little stored procedures that runs from an async database command. It is a bit old fashioned but LinqToSql does not have any built-in async support. I want avoid that the clean-up tasks uses processing time of my application.

To create the async command I need the current connection string and add the ASYNC=True value. Easy, I thought. I take the DataContext.Connection.ConnectionString, use it to instantiate a ConnectionStringBuilder and set the Async property. This worked fine as long as I was running with SSI. When I changed to SQL server login I got this error: Error: 18456, Severity: 14, State: 8.

This means that the password is incorrect. That proved to be absolutely correct. The ConnectionString property does not return the password and this is not mentioned in the MSDN.

Working with Enumerators in Business Data Catalog (Moss 2007)

I was just working through some BDC examples and thought that the way DbEntityInstanceEnumerator is being used is a shame. So, instead of each time using the MoveNext method and a cast I wrote these little extension methods:

public static void Foreach(this DbEntityInstanceEnumerator instances, Action<DbEntityInstance> action)
{
    while (instances.MoveNext())
        action(instances.Current as DbEntityInstance);
}

public static void Foreach(this IEntityInstanceEnumerator instances, Action<IEntityInstance> action)
{
    while (instances.MoveNext())
        action(instances.Current);
}

Site Column Web Part

Did you ever wonder if you could plug a SharePoint Field Control into a Web Part?

I did today – and yes, you can. The code is trivial. Instantiate the Field Control and add it to the controls collection. Ok, maybe normal people do not do that. But I wanted to add some flexibility to the page layout (Moss in a WCM scenario) so that site administrators can move the page content around.

Check-out the simplified example of my Site Column Web Part. I did not compile the code below, but you will get the idea. The administrator will need to assign the FieldName property correctly. Either you could create a custom Editor Part so that the administrator only can choose between the available site columns that have not yet a Field Control assigned or you could create a web part for each site column with the FieldName somewhat hard-coded.

namespace SiteColumnWebPart
{
    public class SiteColumnWebPart : Microsoft.SharePoint.WebPartPages.WebPart
    {
        private string fieldName = null;

        [Personalizable(PersonalizationScope.Shared)]
        [WebBrowsable(true)]
        [System.ComponentModel.Category("Custom Properties")]
        [WebDisplayName("Field Name")]
        [WebDescription("Name of the site column you want to bind to.")]
        public string FieldName
        {
            get { return (fieldName == null) ? “SomeSiteColumnsName" : fieldName; }
            set { fieldName = value; }
        }

        protected override void CreateChildControls()
        {
              base.CreateChildControls();
              RichHtmlField c = new RichHtmlField();
              c.FieldName = FieldName;
              this.Controls.Add(c);
        }
    }
}

LINQ to SQL unit testing

Here is a suggestion on how you easily can create integration tests when working with LINQ to SQL and the MSTest unit test framework. I use transactions to roll-back the changes made during the test run. The preparation of the data context and the clean-up is done once for all tests. This is not a clean unit test approach but it minimizes the time spent on preparing the data.

namespace SomeNamespace.Test.IntegrationTests
{
    [TestClass]
    public class SomeTestClass
    {
        private static DataContext Dc;

        [ClassInitialize]
        public static void Initialize(TestContext context)
        {
            // Setup database.
            Dc = new DataContext();
            Dc.Connection.Open();
            Dc.Transaction = Dc.Connection.BeginTransaction();
            // Clear tables.
            Dc.SomeTable.DeleteAllOnSubmit(Dc.SomeTable);
            Dc.SubmitChanges();
        }

        [ClassCleanup]
        public static void Cleanup()
        {
            // Restore database.
            if (Dc == null || Dc.Transaction == null || Dc.Connection == null)
            {
                Debug.WriteLine("Database clean-up failed.");
                return;
            }
            Dc.Transaction.Rollback();
            Dc.Dispose();
        }

        [TestMethod]
        public void SomeMethodUnderTest_NoInput_NoRows()
        {
            Assert.AreEqual(0, Dc.SomeTable.Count());
        }
    }
}