Tuesday 30 March 2010

Building SharePoint Solutions with TFS Build 2008

After deploying Team Foundation Server 2008 in late 2008, the project’s second phase was intended to implement Team Build to automate builds and establish a continuous integration process for our key projects. At the time, the initial investigation work determined this to be all too hard, which is unfortunate because I’ve been doing the builds for our team ever since!

With our first-stab process templates and first-stab branch structure now being revised, and with Team System 2010 due any day now, I decided to review what “too hard” really meant and see for myself what it would take to get the 2008 build server running. In our case, we’re not only dealing with SharePoint builds and the need to output a .wsp/.cab solution file but a VS solution structure first established in 2006. One of our projects also builds a deployment (.msi) file.

Although I have yet to get the solutions proper tweaked and loaded up, I have got a relatively simple MSBuild-based SharePoint solution building, batch file post-build events and all. This post is a summary of my notes and findings from my efforts to get this working.

Andrew Connell has a detailed post on how to get MSBuild playing with MakeCab.exe, so I’d recommend starting there if you don’t have the MSBuild bits in place already. From that point I largely followed Jeremy Jameson’s advice to get the .targets and .ddf files working across desktop builds and TFS Build. For more information about MakeCab and DDF files, I highly recommend you read the MakeCab documentation linked from my earlier post.

Relative Paths

Perhaps most critically, note Visual Studio and TFS will build your source from different locations. This rules out using absolute paths in any of your scripts or things will break; you also need to be very careful with relative paths because even that old stalwart, \bin, doesn’t exist in the TFS Build environment (it instead uses \binaries).

What this means is relative paths are acceptable but you do need to suck them out of the environment variables/macros set by Visual Studio/Team Build—primarily $(OutDir) or whatever works best for you.

OutDir Cleanup

$(OutDir) may include spaces and will likely include a trailing slash, which needs to be cleaned up by adding quotes to avoid issues with spaces and removing the trailing slash before it can be supplied to MakeCab. I added this to the PropertyGroup element in my .targets file:

<QuotedOutDir>"$(OutDir)"</QuotedOutDir>
<QuotedOutDir Condition="HasTrailingSlash($(OutDir))">"$(OutDir)."</QuotedOutDir>

The property is subsequently supplied in the target:

<Exec Command="$(MakeCabPath) /F $(DDFName) /D CabinetNameTemplate=$(CabinetOutputFileName) /D Out_Dir=$(QuotedOutDir)" />

Remember, any changes to the .targets file will not be detected until the .csproj file is unloaded and reloaded (right-click in Visual Studio).

DDF and .Option Explicit

The Out_Dir parameter I supply to MakeCab (or rather the .ddf file, more precisely) is a custom variable unknown to MakeCab. With .Option Explicit enabled, you would normally be required to include a statement like this:

.define Out_Dir=

My understanding is MakeCab doesn’t support this, however, and .Option Explicit must therefore be turned off or you'll end up with an error like this:

ERROR: Option Explicit and variable not defined: OUT_DIR

Reference Other Projects and .Set SourceDir

To get the assemblies and debug symbols from the bin directories in your other projects to the deployment project, it’s easiest to to have the deployment project reference those projects; the DLLs and pdb files will then be copied to the deployment project’s bin directory—which is also the value of $(OutDir).

Instead of then referencing these files using a long path in the .ddf, they can now be referenced simply by name. You’ll first need to configure the SourceDir in the DDF, however before resetting it for the remaining files:

.Set SourceDir=%Out_Dir%
MyControls.dll
MyControls.pdb
.Set SourceDir=

Dummy Code File

I make use of a dedicated class library project responsible for wrangling the MSBuild bits, post-build events, and to otherwise contain all the SharePoint solution and feature bits. As a class library this project wants to compile and output an assembly but with no source code in the project, TFS Build will throw up a compiler error (everything will work fine on the desktop, however):

CSC(0,0): error CS2008: No inputs specified

To work around this, add an empty .cs file to the project (it’s a hack).

Long File Paths

Windows’ own maximum path length rules will kick in once you hit 259 or 260 characters. If you’re already working in a deep branch with a matching file system path structure, Team Build will likely cause you problems when it starts shifting sources into is own working directories below C:\Documents and Settings\{service}\Local Settings\Temp\. Consider the changes suggested by Aaron Hallberg to work around this problem.

Good luck!

Manifest.xml and Root Paths

The Bonobo Journal has a nice little table detailing where the files in your solution manifest will end up once deployed to the SharePoint root. I’ve taken the liberty of reproducing the table here and expanding it for future reference:

Manifest Element Location’s Root
ApplicationResourceFile 12\Resources
Assembly Web application bin or GAC
DwpFile Virtual directory\wpcatalog
FeatureManifest 12\TEMPLATE\FEATURES
Resource 12\TEMPLATE\FEATURES
ResourceFile 12\TEMPLATE\FEATURES
RootFile 12
SiteDefinitionManifest 12\TEMPLATE\SiteTemplates
TemplateFile 12\TEMPLATE
WebTempFile 12\TEMPLATE

MakeCab.exe SDK and DDF Reference

I just stumbled across an ancient but incredibly useful post from one of my favourite bloggers:DDF Command and Variable Syntax.

If you’re a tool monkey this won’t be of any interest but if you cut your .ddf files by hand or like to know what’s going on under the covers in the creation of your .wsp/.cab files, this post calls out some very relevant points from the Microsoft Cabinet SDK—which is also worth downloading, btw. If you ever wanted a reference to MakeCab or those weird ddf files, this is it.

Remove quotes from batch file parameters

No doubt a boring old school trick for the Powershell users among you, so, like, over batch files but handy in case of emergency:

If you supply a quoted parameter to a batch file (e.g. a directory path) and later need to remove the quotes to accommodate additional manipulation, handle the incoming parameter as %~1 instead of %1. 

To remove a trailing slash, do this:

IF "%SOLUTION_DIR:~-1%"=="\" SET SOLUTION_DIR=%SOLUTION_DIR:~0,-1%

Thanks Chuck and Jon!

Saturday 27 March 2010

SharePoint 2010 Ignite Online for Partners

The SharePoint Team Blog recently advertised the availability of the SharePoint 2010 Ignite Online training materials for partners. Apart from the content being current and very detailed for those of us looking to upgrade our skills, the videos are presented by a number of trusted SharePoint professionals (notably Ted Pattison and Andrew Connell from Critical Path Training).

Most of the videos are reasonably brief at fifteen minutes or so and familiarity with WSS3.0/MOSS 2007 is assumed so there’s no beating around the bush. Topics are divided into admin and dev and the dev-specific topics cover feature and solution changes, lists, events, the client object mode, LINQ to SQL, and more.

I always find videos a bit hit and miss but everything I’ve watched so far has been top quality, informative, and potentially useful. Definitely work checking out if you’re a Microsoft partner or working under the partner program.

Wednesday 17 March 2010

Selecting a delimited list of rows with other values in SQL

There are a number of really detailed posts out there describing an easy way to concatenate the values from a specific column into a delimited list without resorting to a cursor. Using coalesce and a temporary variable, the activity is child’s play:

Assuming you have a table named MyTable with a column named FirstName:

DECLARE @List varchar(MAX)

SELECT 
        @List = COALESCE(@List + ', ', '') + CAST (FirstName as varchar (200))
FROM
        MyTable

select @List

the above will return you something like “Ben, James, Ed”.

Nice and easy, right? But how do you use this in a non-trivial situation where you need to include additional columns in the select list? You can’t do this in the above statement (as far as I’m aware); in my case today, I wanted to a) avoid declaring the temporary variable and b) join the results to another table by Id.

for xml path ('') will help in the first case:

select cast(FirstName as varchar (200)) + ', ' from MyTable
for xml path ('')

but you’ll still get stuck trying to join the results.

Thankfully Arnie Rowland has a really concise SQL wiki post describing how accomplish this feat in SQL Server 2000 and 2005/2008.

Assuming this table:

CREATE TABLE TeamInfo
( MemberID int IDENTITY,
TeamID int,
FirstName varchar(50)
)


Arnie’s query looks like this:



SELECT
t1.TeamID,
MemberList = substring((SELECT ', ' + FirstName
FROM TeamInfo t2
WHERE t1.TeamID = t2.TeamID
ORDER BY
TeamID,
FirstName
FOR XML PATH('')
), 3, 1000 )FROM TeamInfo t1
GROUP BY TeamID


And returns this result:



TeamID     MemberList
1 Bob, Jim, Mary
2 Ellen, Ralph, Sue
3 Bill, Linda


Note the substring function drops the leading delimiter and limits the result to just under 1000 characters. Also TeamInfo doesn’t have to be the same table, it can be any table with same IDs used in the t2 query.

Tuesday 16 March 2010

C# var recommendations

With a recent move to .NET 3.5 we’re beginning to review our coding standard and consider some of the things C# 3.0 makes available to new and experienced developers alike. One of these things is the contentious var keyword.

‘var’ seems to instil fear into some developers with others viewing it as total awesomeness. Like all things, var has its place for storing anonymously typed objects returned from the likes of a LINQ query but can it really be misused?

While var does at first glance appear to be a merger of JavaScript and VB with C#, it’s important to note the use of the var keyword must comply with the following:

  • A variable declared using the var keyword must be initialised within the same statement
  • An object with a different type cannot be assigned in a subsequent statement
  • Variables declared with a type of ‘var’ are strongly-typed and compile-time checked; instead of having to determine the type yourself, the compiler does it for you
  • Intellisense will continue to work wherever the variable is used
  • The var keyword can only be used for local variables within a method

Commenters responding to a blog post by Steven Wellens on the use of the var keyword also cited an improved refactoring experience; I partially agree with this point however Visual Studio’s refactoring tools largely take care of those problems.

The main reasoning by commenters was for (and against) readability and I agree strongly that using var reduces “noise” words in the code. In my view, this improves the ability to easily scan the code.

The example statement Steven supplied was claimed to be unreadable—but primarily for the poor name chosen for the GetData method:

var data = GetData();

If the method is renamed, this does remain a gray area unless it also includes type information in the name, which is yucky. Removing type information does allow intent to shine through of course—not a bad thing.

With this preface, here are the draft recommendations for use of the var keyword in our shop:

  • Do declare the type if it's useful otherwise use 'var' to avoid "noise" where type information is unimportant or can be read in the object's initialisation. Eg: var cat = new Cat ();
  • Do use 'var' to highlight intent over implementation detail
  • Do use 'var' as required for anonymous types
  • Consider readability; avoid examples like var data = GetData(); where the type returned by GetData() is not obvious

References

Use EnableValidator to Manage an ASP.NET Validator in Client Script

I’m a big fan of the ASP.NET validator controls—not because they’re complete and wonderful but because they’re convenient when they work as expected and otherwise keep me on my toes when something out of the ordinary is required.

Today brought forth a requirement to hide the State field on a form when the country wasn’t set to Australia. Because the State field also has a RequiredFieldValidator attached, I would have to disable the validator server-side in some cases during the initial page load (depending on the data being pre-filled) and disable it client-side as the user interacts with the form. Failing to do so would prevent the form from posting back when the State field was hidden.

One of the main reasons I like these validation controls so much is because of the way they just work client-side and server side; in the client arena, everything occurs as Javascript and that was enough for me to assume, in this case, I could manage the validator through Javascript. I wasn’t certain this would be possible (or at least simple) but fortunately I won’t lose any sleep over this one tonight ;) As I found out, changes to a validation control in one context are even reflected in the other—nice!

The magic all happens with the mysterious ValidatorEnable(validator, bool) function. I have no idea where this function comes from and don’t really care but it allows you to enable or disable a validator in Javascript code by supplying the validator object and a boolean value indicating whether it should be enabled (true) or disabled (false).

It’s important to remember the first parameter is the validator object itself, not its ID. You’ll need to locate the validator object by ID (or using some other means) of course, but that’s where the ClientID property comes in handy:

var stateRequiredFieldValidatorId = "<%=rqdState.ClientID %>";

For additional information check out the ancient (circa 2002) “ASP.NET Validation in Depth” article on MSDN. Jonas Bush also has a concise example.

Monday 15 March 2010

Retrieve just a URI’s path

Need to get the path component of a URI without the domain, port, or query string? Use Request.Url.AbsolutePath. Despite its name it returns a relative URL:

So http://blog.mediawhole.com/somepage.aspx?a=b becomes /somepage.aspx.

How to return an anonymous type from a method

Current .NET lore would have you believe you can’t return an anonymous type from a method but Tomas confirms it is possible by casting and cites the C# 3.0 spec:

Within the same program, two anonymous object initializers that specify a sequence of properties of the same names and types in the same order will produce instances of the same anonymous type.

Tomas also qualifies his interesting post with a request to let him know if a good reason can be found for using this technique!

In my own work I recently wanted to call a method that returns what I would ultimately implement as a data transfer object: a strongly-typed collection of structured, related data. I considered out parameters and collections but neither option really struck my fancy; a DTO would likely be the preferred option at the expense of introducing another class, maintenance overheads, etc.

In the hunt for a better, modern solution, the concept of returning an anonymous type appealed—apart from the problems anonymity introduces! While I understand how .NET constructs anonymous types and reuses those that identical, the one major problem I see with Tomas’ solution is the requirement that both “sender” and “receiver” not only know about the so-called anonymous type but actually define it in two places. This just seems like more overhead to me so I abandoned this approach before long.

Friday 5 March 2010

Enable testing of web services

I’m bound to forever forget how to switch on (or off) the ASP.NET web service test screen and documentation options. For future reference:

http://msdn.microsoft.com/en-us/library/b2c0ew36(VS.85).aspx

or add this to the system.web section of the service’s web.config file:

<webServices>
        <protocols>
            <add name="HttpSoap12"/>
            <add name="HttpSoap"/>
            <add name="HttpGet"/>
            <add name="HttpPost"/>
        </protocols>
</webServices>

Wednesday 3 March 2010

Fiddler redirects localhost. to IP address

Debugging local HTTP traffic using Fiddler, while not intuitive, is easily accomplished by adding a dot to your localhost address. In other words, http://localhost:1234 becomes http://localhost.:1234 and traffic is captured like it always is.

This wasn’t quite working for me this morning: localhost was resolving to the IP address of the host machine, without the port or directory bits. I’d earlier disabled capturing by clicking the “Capturing” button in the status bar and that seemed to be causing this behaviour; enabling capture once again and the old dot trick worked again.

Incidentally, after closing Fiddler, I noticed IE8 does the same thing so this is likely a browser behaviour rather than a Fiddler oddity.