Friday, December 7, 2007

Just Say No To Software Development Reference Books!

Someone asked today if I could recommend any TFS books. "I don't have any books- I have Google," I replied. This was sort of a flippant answer but it's the truth. I didn't know how prescient this statement was until I ran into a problem later in the day.

I've recently been working on bidirectional communication between Sharepoint 3.0 and TFS 2005. Toward the end of the project I ran into a problem relating a TFS display name (such as 'John Doe') to a Sharepoint account name (think 'DOMAIN\doejoh'). After a little research I developed a theory that the IGroupSecurityService interface was going to play an integral part in my quest for account names. I plugged 'TFS IGroupSecurityService' into Google and out popped the exact answer that I was looking for. I'm not a Google cheerleader and I love books, but how could I have found this information in some overpriced, overlong book from WROX, Apress, O'Reilly, etc.?

I read a lot of books about history. I think that it's fascinating that we as a species have quite suddenly developed this ability to have all of this information available in an easily searchable format.

Thursday, December 6, 2007

Most Exciting Visual Studio 2008 Feature


There's a lot of talk about LINQ, WPF, WCF, etc., but my favorite new Visual Studio 2008 / .NET 3.5 feature so far is the 'Remove Unused Usings' function.

Tuesday, November 20, 2007

Manipulating SQL Server Reporting Services files with PowerShell

My company is addicted to reports.

We recently discovered that there was a bug involving Print Layout mode in the Report Viewer component and SQL Server 2005 Service Pack 2. I was suddenly confronted with the daunting prospect of altering 230+ SQL Server 2005 Reporting Services (SSRS) reports in VS2005, or at least assigning the task to someone. Knowing that SSRS report files are just XML, I suspected that there was an easier way. I had been looking for an excuse to do something with
Powershell and this was it.

After a quick inspection of a few files I was able to build the script below to make the necessary changes to avoid the Print Layout bug. There's definitely room for refactoring because the below script is fairly procedural. But time trumped elegance and Powershell saved a lot of time. The only problem I encountered was that my new elements were being created with an empty xmlns attribute. I'd seen this problem before, but couldn't remember the resolution initially. Google reminded me that I needed to add the new elements to the same namespace as the existing XML document.


$outputDir = "C:\[output directory]\"
$files = Get-ChildItem C:\[path to reports]\*.* -include *.rdl
Write-Host("Found " + $files.Length + " files in " + $files[0].DirectoryName + " directory.`n`r")
foreach ($f in $files)
{
$numberOfModifications = 0
Write-Host($f)
$fileContents = Get-Content $f
$xdoc = New-Object -TypeName System.Xml.XmlDocument
$xdoc.LoadXml($fileContents)

Write-Host("`tBody element")
# Determine whether Body.Style element exists. Apparently Body always exists.
if($xdoc.Report.Body.Style -eq $null)
{
Write-Host("`t`tCreating Body.Style element.")
$styleNode = $xdoc.CreateElement("Style","http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition")
$xdoc.Report.Body.AppendChild($styleNode)
}
else
{
Write-Host("`t`tBody.Style element found.")
$styleNode = $xdoc.Report.Body.Style
}

if(($styleNode -ne $null) -and ($styleNode.BackgroundColor -eq $null))
{
# Note that it's extremely important to qualify the namespace when adding a new element
$xn = $xdoc.CreateElement("BackgroundColor","http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition")
$xn.set_InnerXml("White")
$styleNode.AppendChild($xn)
# $xdoc.Save($outputDir + $f.Name)
$numberOfModifications += 1
}
else
{
Write-Host("`t`tNo modifications necessary.")
}

$styleNode = $null
$xn = $null

#
# PageHeader
#
Write-Host("`tPageHeader element")
if($xdoc.Report.PageHeader -ne $null)
{
$headerNode = $xdoc.Report.PageHeader

# Determine whether PageHeader.Style element exists.
if($headerNode.Style -eq $null)
{
Write-Host("`t`tCreating PageHeader.Style element.")
$styleNode = $xdoc.CreateElement("Style","http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition")
$headerNode.AppendChild($styleNode)
}
else
{
Write-Host("`t`tPageHeader.Style element found.")
$styleNode = $headerNode.Style
}

if(($styleNode -ne $null) -and ($styleNode.BackgroundColor -eq $null))
{
# Note that it's extremely important to qualify the namespace when adding a new element
$xn = $xdoc.CreateElement("BackgroundColor","http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition")
$xn.set_InnerXml("White")
$styleNode.AppendChild($xn)
$numberOfModifications += 1
}
else
{
Write-Host("`t`tNo modifications necessary.")
}
}
else
{
Write-Host("`t`tPageHeader element not found.")
}

$styleNode = $null
$xn = $null


#
# PageFooter
#
Write-Host("`tPageFooter element")
if($xdoc.Report.PageFooter -ne $null)
{
$footerNode = $xdoc.Report.PageFooter

# Determine whether PageHeader.Style element exists.
if($footerNode.Style -eq $null)
{
Write-Host("`t`tCreating PageFooter.Style element.")
$styleNode = $xdoc.CreateElement("Style","http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition")
$footerNode.AppendChild($styleNode)
}
else
{
Write-Host("`t`tPageFooter.Style element found.")
$styleNode = $footerNode.Style
}

if(($styleNode -ne $null) -and ($styleNode.BackgroundColor -eq $null))
{
# Note that it's extremely important to qualify the namespace when adding a new element
$xn = $xdoc.CreateElement("BackgroundColor","http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition")
$xn.set_InnerXml("White")
$styleNode.AppendChild($xn)
$numberOfModifications += 1
}
else
{
Write-Host("`t`tNo modifications necessary.")
}
}
else
{
Write-Host("`t`tPageFooter element not found.")
}

$styleNode = $null
$xn = $null


# Done with this iteration.
if($numberOfModifications -gt 0)
{
Write-Host("`tSaving file.")
$xdoc.Save($outputDir + $f.Name)
}
else
{
Write-Host("`tNo file changes necessary.")
}
$xdoc = $null
}

Tuesday, January 16, 2007

ButtonCollection iteration and PostBacks using WatiN

I've been playing with the .NET 2.0 WatiN framework and ran into a problem that might be of interest to someone.

My application under test is .NET 1.1. I was trying to click through a series of buttons in a DataGrid and also click 'OK' on the confirmation dialog that would appear after the DataGrid
button was clicked. I was generally only able to go through this process once, though, before receiving the following exception:

System.UnauthorizedAccessException: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)).

My code to loop through the DataGrid buttons at the time was as follows:

ButtonCollection collection = ie.Buttons.Filter(Find.ByValue("Delete"));
if (collection.Length > 0)
{
foreach (Button button in collection)
{
//ConfirmDialogHandler confirmDialogHandler = new ConfirmDialogHandler();

using (new UseDialogOnce(ie.DialogWatcher, new SimpleJavaDialogHandler(false)))
{
button.Click();
//button.ClickNoWait();
//confirmDialogHandler.WaitUntilExists();
//confirmDialogHandler.OKButton.Click();
//ie.WaitForComplete();
}
}
}

As you can see from the commented-out code I also tried the ConfirmDialogHandler as described in this email.


I finally got my concept to work by using this code:

while (ie.Buttons.Filter(Find.ByValue("Delete")).Length > 0)
{
Button button = ie.Buttons.Filter(Find.ByValue("Delete"))[0];
using (new UseDialogOnce(ie.DialogWatcher, new SimpleJavaDialogHandler(false)))
{
button.Click();
}
}


I assume that my problem was related to the just-clicked button no longer being part of the original ButtonCollection after PostBack and that this created some type of concurrency issue somewhere in the ASP.NET / WatiN innards.

Hope that this helps someone.