Saturday, January 30, 2010

More bad luck

As my attempts to learn Sync Framework continue, I find even more issues along the way, this time with the SQLCE runtime. The basic scenario boils down to attempting to fill a Detail view from a dataset linked to a SQL Compact Edition datasource. When the application is run, an error message to the effect of

Unable to load DLL 'sqlceme35.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E).

is displayed, and the application crashes. After trying many different things to solve the problem, a simple google search on the exact error yields the solution. Basically it is a problem on Vista and Win7 x64 environments, and simply requires you to install SQl Compact Edition SP1 found here http://www.microsoft.com/downloads/details.aspx?FamilyId=DC614AEE-7E1C-4881-9C32-3A6CE53384D9&displaylang=en#filelist.

I guess I was just surprised that it was not included as part of Visual Studio 2008 SP1.

A colleague of mine Mitch Denny has told me that “If I was going to pick a person to clear a minefield you would be that person”, I replied that I was hoping it meant he believed I had special unique skills for finding and defusing mines, and not just that I am expendable.

Wednesday, January 27, 2010

TFS 2010 Console Application and the .Net 4.0 Client Profile

One little frustration that has caught me twice now (and so it’s time to blog it so I remember it), is that when you create a Console Application and then start adding references to any of the TFS API libraries to it (such as Microsoft.TeamFoundation.Client.dll), if yoiu just accept all the defaults for a Console Application in Visual Studio 2010 Beta 2, you get this warning

Warning    1    The referenced assembly "Microsoft.TeamFoundation.Client, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" could not be resolved because it has a dependency on "System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" which is not in the currently targeted framework ".NETFramework,Version=v4.0,Profile=Client".    BasicEventListener

 

and the application will not compile.

The issue is the .Net 4.0 Client Profile does not contain the reference to System.Web, which is required by the TFS API. This is simple to fix, just go to the Properties of your project, and for the Target Framework, select “.Net Framework 4” instead of the “.Net Framework 4 Client Profile”.

The unluckiest developer alive

Sometimes I think I should be a tester for developer tools. For some reason I have the uncanny knack of stumbling onto the most obscure bugs in Visual Studio.

I am currently trying to learn Microsoft Sync Framework, and decided (after watching a good video on how to do it) to set up an ADO.Net Sync Services Local Database Cache. I went through the process step by step, the only 2 differences between my environment and the one demonstrated in the video was that I was running SQL Server 2008, and I was using the AdventureWorks2008 database.

The procedure I followed was this…

  1. Create a new Winforms Application
  2. Add A new Local Database Cache (this starts the wizard)
  3. select the AdventureWorks database
  4. Select the Production.Products, Production.ProductDescription, Production.ProductPhoto tables to add to the cache.
  5. Click OK.

Everything else I left as the default configuration. The result:

Data Synchronization Settings: Incorrect syntax near '.'.

So I figure it’s something to do with my environment. I spent about half an hour Googling and looking at forums before I decided that I must be the first person ever to hit this bug. Next I tried to see if it was because of something in my environment, I checked my setup

Windows 7 64 bit; SQL Server 2008 SP1; Visual Studio 2008 SP1; Sync Framework 2.0 x64;

The only thing I could think might be an issue was the Sync Framework 2.0 x64, so I uninstalled and installed the x86 version. Still the same problem.

I then opened SQL Profiler and tried to see if anything was obvious there, but concluded that it was all OK (I later discovered that I didn’t look closely enough at this).

I then cracked open the wizard dlls in reflector to see if there was any help there, but alas, theat was pretty much a dead end too.

I even posted an article on the Sync Framework Forum.

Frustrated, I decided that it had to be my own environment that was causing the problem, so I set about creating a virtual environment on GoGrid. Once I had managed to get everything installed, I tried just the Production.Products table this time, and got the exact same error.

I then tried creating a completely new table in a completely new database, and, surprise surprise, it actually worked… well, almost. It got past the wizard, but the code it generated would not compile. It appears that I stumbled on yet another bug in the Sync Framework wizard, if you use a table called “SyncTable”, the generated code is broken.

Encouraged by the “success”, I pressed on undeterred. I finally got everything working by creating a table called “MyTable”.

Not satisfied that I had made it work (finally), I decided to try and track down the bug. After about an hour or so of playing around and not having any success, I finally cracked open SQL Profiler again and found that the last statement executed before it died when trying to create a local database cache for the Production.Product table was the following

SELECT * FROM [Product]ion.[Product]

Obviously the code that works out the schema and table name needs some reworking.

Saturday, January 09, 2010

Using the TFS 2010 SDK (Beta 2)

With significant changes to the way TFS 2010 works, especially in relation to Team Build and the new concept of Projects Collections, the API has necessarily adapted to account for these changes. For developers wanting to use these new API’s, there is currently very little in the way of documentation. The TFS SDK code gallery site has a small amount of examples, as well as the beginnings of the API documentation, which although sparse, because of reasonably clear naming conventions actually offers users some guidance when attempting to write integration software for TFS 2010. There are also a few other bloggers around who have some posted code examples.

I have been looking at re-writing TFS Dependency Replicator for TFS 2010, and in so doing have created a very simple application to enumerate some of the objects I am interested in.

In the interests of helping other developers, here is my source code. You can also download the full solution from here.

To run it, you must have .Net 4.0 beta 2 and TFS 2010 Explorer Beta 2 installed on your machine.

class Program
{
static void Main(string[] args)
{

// Get a list of all registered Project Collections
Console.WriteLine("Project Collections");
var collections = RegisteredInstances.GetProjectCollections();

foreach (var collection in collections)
{
Console.WriteLine("Connecting to TFS Server {0} at {1}", collection.Name, collection.Uri.ToString());

// connect to tfs
using (var tfs = new TeamFoundationServer(collection.Uri))
{
tfs.EnsureAuthenticated();
Console.WriteLine("Successfully connected to {0}", tfs.Uri.ToString());

// Get items of interest from TFS
EnumerateServer(tfs);
}
}

Console.ReadLine();
}

private static void EnumerateServer(TeamFoundationServer tfs)
{
// Get all of the Team Projects
var structureService = tfs.GetService();
var teamProjects = structureService.ListAllProjects();

Console.WriteLine("Team Projects");

foreach (var teamProject in teamProjects)
{
Console.WriteLine("\tName: {0}", teamProject.Name);
}

// extract all the work items
// NOTE: this uses wiql to query the work item store
Console.WriteLine("Work Items");
var workItemStore = tfs.GetService();
var workItems = workItemStore.Query(
"SELECT [ID], [Title] FROM WorkItems");

foreach (WorkItem workItem in workItems)
{
Console.WriteLine("\tID: {0}\tTitle: {1}", workItem.Id, workItem.Title);
}

// Get the build servers
Console.WriteLine("Build Servers");
var buildServer = tfs.GetService();
var buildControllers = buildServer.QueryBuildControllers(true);

// Get all the build controllerrs
foreach (var buildController in buildControllers)
{
Console.WriteLine("\tController: {0}", buildController.Name);

// Get all the build agents
foreach (var agent in buildController.Agents)
{
Console.WriteLine("\t\tAgent: {0}, Team Project: {1}", agent.Name, agent.TeamProject);
}
}

foreach (var teamProject in teamProjects)
{
// Get each build definition for this Team Project
var buildDefinitions = buildServer.QueryBuildDefinitions(teamProject.Name);

foreach (var buildDefinition in buildDefinitions)
{
Console.WriteLine("\tBuild: {0}", buildDefinition.Name);

// Get all build Details for this build definition
var buildDetails = buildDefinition.QueryBuilds();

foreach (var buildDetail in buildDetails)
{
Console.WriteLine("\t\tBuild Number: {0} Build Status: {1}", buildDetail.BuildNumber, buildDetail.Status);
}
}
}

}
}