RSS 2.0
# Tuesday, April 16, 2013

Since upgrading to TFS 2012.2 (from Update 1) I have been seeing the following error in a couple of places:

Server was unable to process request. ---> 
There was an error generating the XML document. ---> 
TF20507: The string argument contains a character that is not valid:'u8203'. 
Correct the argument, and then try the operation again.

It first appeared when loading VS2012.2 with TFS Sidekicks 4.5 installed. It also appeared when I called IIdentityManagementService.ReadIdentities() via the TFS 2012 Object Model. I guess this is what TFS Sidekicks is calling under the covers.

It turned out that this was caused by the Zero Width Space Unicode character (\u200b) been present in a Team's Description field. Some of our users had copied and pasted into there and brought it along from somewhere, where it came from I'll probably never know.

To track down which Team had this problem was quite a trek. Firstly, this doesn't happen using the V10 (TFS 2010) Object Model, only the V11 Team Foundation client assembly references.

Using the following code in my TFS Template (2012 Version) as a base:

var managementService = 
    tfs.GetService<IIdentityManagementService>();
var members =
    managementService
    .ReadIdentity(
        GroupWellKnownDescriptors.EveryoneGroup, 
        MembershipQuery.Expanded,
        ReadIdentityOptions.None);
    .Members;

I then call this method to get the crash:

var nodeMembers = 
    managementService
    .ReadIdentities(
        members, 
        MembershipQuery.Expanded, 
        ReadIdentityOptions.ExtendedProperties);

The problem is, in my TFS Instance, there are 634 "members" and I had no idea which one might be causing the problem:

Replacing the above line with a simple loop and try…catch block reduced that for me:

foreach (var member in members)
{
    try
    {
        var nodeMembers = 
            managementService
            .ReadIdentities(
                new [] { member, }, 
                MembershipQuery.Expanded, 
                ReadIdentityOptions.ExtendedProperties);
    }
    catch (Exception e)
    {
        e.Dump(a.Identifier);
    }
}

I now had the Identity of the member with an issue, the problem I now faced was getting the name of the member.

A typical Identity looks like this:

S-1-9-1441374244-17626364-2447400142-3087036873-88942238-1-3433204373-3394714127-2914434643-4144131896

In the end I fired up SQL Profiler, pointed it at TFS and re-ran my code snippet. I then stopped the trace and searched for the first part my Identity: 1441374244. This led me to the following SQL statement:

declare @p3 dbo.typ_KeyValuePairInt32StringVarcharTable;
insert into @p3 values(0,'S-1-9-1441374244-2649122007-3436464326-2922169763-974421344-0-0-0-0-3');

exec prc_ReadGroups 
    @partitionId=1,
    @scopeId='5c5c4fe4-eba6-4899-87f1-f2f8a1802a6e',
    @groupSids=@p3,
    @groupIds=default;

By copying this into SQL Management Studio and running it against the Tfs_Configuration database (in a begin tran…rollback tran block, of course) I was able to get details of the Identity, in my case it was a Team.

I viewed the results of this Query as "Text" from SQL Management Studio and pasted them into a blank UTF-8 document in Notepad++. Flicking to the trusty Hex Editor plugin I could soon see some characters that did not belong in the Description text. These were the u200b Zero Width Spaces.

To fix this I just opened up my Team Web Access, navigated to the Team's page and deleted the Description, re-typed it and saved. Re-running my Linqpad sample proved the issue was solved.

I've logged this as a Connect Bug as I am sure it is not intentional and it only started happening since installing in Update 2.

Tuesday, April 16, 2013 9:58:02 PM (GMT Daylight Time, UTC+01:00)  #    Comments [0]
TFS
# Monday, April 01, 2013

This post is going to cover a combination of two the things I love, Linqpad and Geocaching.

If you're not familiar with Geocaching, but have any love for the outdoors, then I heartily recommend you try it. It's pretty much treasure hunting for grown-ups and using grown-up toys. It's also great for the family, I never go without my son with me.

If you're not familiar with Linqpad, go get acquainted, I use it for so many different roles, in this case, scripting C#.

As I am only an amateur Geocacher, my GPS device of choice is my Windows Phone (currently a Lumia 920 – which I also love) and GPS Tuner's Outdoor Navigation. Outdoor Navigation is ace, it has many features making a great companion when out and about. However, it does not have full integration with Geocaching.com, so you have to use Dropbox to synchronise a "loc" file downloaded from Geocaching.com.

This brings us to the problem, each loc file is one Point of Interest in Outdoor Navigation, meaning that if I plan to stock up on (say five) caches before heading out I need to download five files, copy five files to Dropbox, fire up Outdoor Navigation and import each file, one after another from list. Downloading the files is easy enough, my browser only requires one click. Uploading to Dropbox is equally easy, just multi-select and copy. But the import into Outdoor Navigation is not multi-select or fluid to work through the a long list. The import process was starting to get tedious after a while so I decided to dig into the loc files to see if there was a way to speed things up.

Here's a loc file for just one Cache (from today's outing):

<?xml version="1.0" encoding="UTF-8"?>
<loc version="1.0" src="Groundspeak">
    <waypoint>
        <name id="GC37092">
            <![CDATA[A Longer Dog Walk #1 by The Briar Rose]]>
        </name>
        <coord lat="53.602717" lon="-1.79085"/>
        <type>Geocache</type>
        <link text="Cache Details">http://www.geocaching.com/seek/cache_details.aspx?wp=GC37092</link>
        <difficulty>1.5</difficulty>
        <terrain>1</terrain>
        <container>2</container>
    </waypoint>
</loc>

It's an XML document with a "loc" root element and a "waypoint" for the Point of Interest. After a bit of experimentation I found I could put any number of "waypoint" elements into the document and that Outdoor Navigation would only have to import one file that contained any number of Points of Interest. Bingo!

Now I had a way to import just one file if, but to do that I needed to combine all the individual loc files from Geocaching.com. The next issue was how to combine them, enter Linqpad, as always.

Using a bit of Linq-Xml I was able to load each file, grab the "waypoint" element(s) and then combine them into a new XML document that I could save to disk and even auto upload to Dropbox – providing I used the Dropbox Application for Windows.

First. Load all the loc files I have downloaded.

var locationElements = 
  Directory
  .EnumerateFiles(folderPath, "*.loc")
  .SelectMany(path => new LocFile(path).GetElements());

There's a bit going on here. We start by grabbing any file with the "loc" extension in the folder stored in the folderPath variable – "Downloads\GC"  in my case – Creating a new instance of the LocFile class calling GetElements() and combining all the results together. I'm using SelectMany instead of Select because I am assuming there could be more than one "waypoint" element in a loc file. What we are left with is an IEnumerable<XElement> containing all the "waypoint" elements from all the files.

Second. Creating the new XDocument:

var combinedLocFiles = 
  new XDocument(
    new XElement("loc",
        new XAttribute("version", "1.0"),
        new XAttribute("src", "Groundspeak"),
        locationElements))
  .Dump();

Here we are creating a new XDocument with a root element of "loc" with the attributes "version" and "src" with the values of "1.0" and "Groundspeak", respectively (the first, second and last lines in the above XML). And then filling the root element with the contents of the locationElements – the "waypoint" elements from the First snippet.

Finally. The LocFile class.

class LocFile
{
    readonly String _filePath;
    public LocFile(String filePath)
    {
        _filePath = filePath;
    }
    
    public ReadOnlyCollection<XElement> GetElements()
    {
        return
            XDocument
            .Load(_filePath)
            .Root
            .Elements()
            .ToList()
            .AsReadOnly();
    }
}

This is the class that represents a loc file and knows how to get the relevant Elements from it.

These three snippets are the meat of the script, everything else is just fluff to get the correct paths and save the output and copy it to Dropbox.

It's pretty easy to use, just fire up Linqpad, load the Script and then run it. Before I start I ensure that there are only the loc files I want to combine in my "folderPath", but other than that, you're away.

If you need to change the paths, that's at the top of the script and is pretty straight forward too.

Download

Here's the link to download the "linq" file from my SkyDrive.

Monday, April 01, 2013 9:00:22 PM (GMT Daylight Time, UTC+01:00)  #    Comments [0]
Geocaching | Linqpad
# Monday, February 18, 2013

Today I found my self in a situation where I needed to initialise a property in MSBuild via the /property:<n>=<v> (short form /p) command line switch to an empty string. The reason I had to do this was to so that I could remove some property from my OutputPath when building on Team Foundation Server.

For Example, in my C Sharp project file I had the following line.

<OutputPath>$(MyProperty)\bin\</OutputPath>
In some scenarios I wanted $(MyProperty) to be "Build" and in other cases I wanted it to be removed.

So the scenarios went a little like this:

<!-- Scenario 1 -->
<OutputPath>Build\bin\</OutputPath>
<!-- Scenario 2 -->
<OutputPath>\bin\</OutputPath>

After a quick visit to Google and thumbing through Inside the Microsoft Build Engine 2nd Edition by Sayed Ibrahim Hashimi, I still could not find a canonical to answer my question. I read something that said an empty string should be two single quotes, but those substituted those into my build causing it to error (it turns out, that was for checking if a variable is empty). In the end I went to the command line and started experimenting. I thought I'd just pop my findings on here in case anyone else has a need for it.

The Answer is to just use PropertyName= and no more.

For Example:

MSBuild /property:MyProperty= MySolution.sln

This syntax works at the start, middle or end of your property command line switch. So these are all valid too:

MSBuild /property:Foo=One;MyProperty= MySolution.sln
MSBuild /property:Foo=One;MyProperty=;Bar=Two MySolution.sln
MSBuild /property:MyProperty=;Bar=Two MySolution.sln

You can check the MSBuild diagnostic (using the /verbosity:diagnostic switch) log to confirm that it worked:

MSBuildUserExtensionsPath = C:\Users\Dave\AppData\Local\Microsoft\MSBuild
MyProperty = 
NUMBER_OF_PROCESSORS = 4
Monday, February 18, 2013 9:21:00 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]
MSBuild
# Wednesday, January 23, 2013

I am a massive fan of Linqpad, especially as a code scratch pad, but it is also very useful for performing queries against the Team Foundation Server SDK.

I regularly find myself wanting to get information out of our TFS Collection via the API, whether it be Build Information, Work Item Queries, Version Information, etc. Occasionally, I also need to update Build Definitions' Process XML en-mass. 

To make my life easier and to enable me to spin up these queries as quick as possible I came up with a "Template" Linqpad script that I can always use as a baseline.

The important code is as follows and the "linq" file has all the references and namespaces I could ever need:

void Main()
{
    const String CollectionAddress = "http://tfsserver:8080/tfs/MyCollection";
    
    using (var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(CollectionAddress)))
    {
        tfs.EnsureAuthenticated();
        var server = tfs.GetService<>();
    }
}

Using

I have this in "My Linqpad Queries" and as soon as I open the file I press Ctrl+Shift+C to clone it to a new query so I don't save change to the "template". Linqpad doesn't yet support "Templates", there is a UserVoice  request for it however.

Once I have the cloned copy I insert the name of the service I plan to call into GetService<>, and then go to work. The API is quite easy to use, and the MSDN documentation is pretty comprehensive. The common services I use are:

  • IBuildServer for Builds
  • VersionControlServer for Source Control
  • WorkItemStore for Work Items

In the downloaded file there are also some XNamespace declarations at the top, which are used when I have to update IBuildDetail.BuildDefinition.ProcessParameters using Linq to Xml. These are the common three I found myself having to declare each time, so I just made them part of the template.

Download

You can download the "linq" file for Linqpad from my SkyDrive here (TFS 2010).

Updated for TFS 2012: You can download the Linqpad script for TFS 2012 here.

Wednesday, January 23, 2013 11:49:46 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]
Linqpad | TFS
# Tuesday, January 08, 2013

So here we go, an actual first post…


I was trying to enable assigning work items to a group on TFS 2010, and after following Ivan Fioravanti's excellent Blog Post on the subject, I still found that my Team Project groups were not appearing in the "Assigned To" dropdown list. After triple checking everything, I noticed the most important point on his blog: "remove VALIDUSER rule, because this is responsible for not showing groups in the dropdown list". I checked again, and yep, the System.AssignedTo field did not have that rule. After a little thinking I eventually searched my Work Item Template Definition XML for the term "VALIDUSER" and bingo, there was another one!

It turned out I also had a "VALIDUSER" rule applied to the initial state transition (i.e. from "" to "New") for the System.AssignedTo field.

This was ensuring that all newly logged bugs must be assigned to a User, and not a Group. If I were testing my change on an existing work item, I would not have noticed the problem.

I removed the extra VALIDUSER – probably added in a moment of over eagerness – and hey presto, everything worked as Ivan said it would!

Tuesday, January 08, 2013 9:25:18 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]
TFS | Work Items

Quick introduction to who I am before I get into technicalities…

I work full-time as a development team leader for a large software firm, working with C#, WinForms and Microsoft SQL Server on an enterprise scale. I am also the "Accidental TFS Admin" for TFS 2010 (soon to be 2012). I plan to share some of my experience that I hope you will find useful.

Topics that I have in mind for coverage:

  • Team Foundation Server/Service (including the API).
  • Useful Linqpad snippets.
  • Windows Phone 8 Development Journey – as I write this, I have not left port, so I will share useful facts as I find them.
  • C#.
  • Unit Testing.

I live in Yorkshire in the United Kingdom.

Tuesday, January 08, 2013 9:23:24 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]
Introduction | Metablogging
About the author
profile for DaveShaw at Stack Overflow, Q&A for professional and enthusiast programmers Dave Shaw is a professional C# developer, blogging about Team Foundation Server, C#, Linq and software development.

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2013
Dave Shaw

Archive
<May 2013>
SunMonTueWedThuFriSat
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678
Statistics
Total Posts: 6
This Year: 6
This Month: 0
This Week: 0
Comments: 0
All Content © 2013, Dave Shaw
DasBlog theme 'Business' created by Christoph De Baene (delarou) and mucked around with by Dave Shaw.