NuGet packages are powerful, but the Transformations are not. Time for a solution!

First of all I don’t want to complain about NuGet. I love it every day and I have created two packages myself that I want be easy to use for everyone. It should be a matter of just Install-Package PackageName and not much more.

I currently have the following packages.

It was last week when I wanted to update the Google Analytics package that I found a problem. When the package was used in a previous version, and I run the update it did a strange thing in the transformation I made before. The transformation I created was transforming the App.xaml file by adding an element to the xaml.

<GoogleAnalyticsService WebPropertyId="UA-12345-6" xmlns="clr-namespace:$rootnamespace$.Analytics" />

The transformation in NuGet aren’t really intelligent. It just adds the above element. And when you have an update for the package it will ad the element again. That’s something I would like to be able to prevent. So are the solutions at the table? Yes there are, you can use PowerShell.

So I took a little bit of time to learn more about PowerShell. I knew it was something like batch, but with support for .NET Libraries. The support for .NET Libraries implies easy use of XML editing, which will be handy Xaml is just Xml.

The first step I had to take is be able to read the content of the App.xaml file from the PowerShell script that can be used as part of the NuGet Package. I’m able to iterate through the project items to find the App.xaml project item which in turn helps me find the local path.

param($installPath, $toolsPath, $package, $project)

# find the App.xaml file 
$config = $project.ProjectItems | where {$_.Name -eq "App.xaml"}

# find its path on the file system 
$localPath = $config.Properties | where {$_.Name -eq "LocalPath"}

That wasn’t that hard, was it? Let’s read the App.xaml file into an Xml object. And look for an element with GoogleAnalyticsService as the name.

# load Web.config as XML 
$xml = New-Object xml 
$xml.Load($localPath.Value)

$gasnode = $xml.SelectNodes("//*") | where { $_.Name -eq "GoogleAnalyticsService"} 
if($gasnode -eq $null) 
{ 
} 

If we didn’t find that kind of element we want to create the element with the example WebPropertyId, just like I previously had in the App.xaml NuGet transformation. First I select the proposed parent element, being the “Application.ApplicationLifetimeObjects” element which I can use to add the to be created XmlNode to. Next I create the XmlNode in the appropriate namespace and add the element the parent element. What’s left is to save the App.xaml file.

$lto = $xml.SelectNodes("//*") | where { $_.Name -eq "Application.ApplicationLifetimeObjects"} 
$gas = $xml.CreateNode("element","analytics:GoogleAnalyticsService","clr-namespace:"+$project.Properties.Item("RootNamespace").Value+".Analytics") 
$gas.SetAttribute("WebPropertyId", "UA-12345-6") 
$lto.AppendChild($gas) 
# save the App.xaml file 
$xml.Save($localPath.Value)

And to be complete, this was the complete Install.ps1 file. Hope this helps all the people who have been struggling to solve the limited Transformations issue.

param($installPath, $toolsPath, $package, $project)

# find the App.xaml file 
$config = $project.ProjectItems | where {$_.Name -eq "App.xaml"}

# find its path on the file system 
$localPath = $config.Properties | where {$_.Name -eq "LocalPath"}

# load Web.config as XML 
$xml = New-Object xml 
$xml.Load($localPath.Value)

$gasnode = $xml.SelectNodes("//*") | where { $_.Name -eq "GoogleAnalyticsService"} 
if($gasnode -eq $null) 
{ 
  $lto = $xml.SelectNodes("//*") | where { $_.Name -eq "Application.ApplicationLifetimeObjects"} 
  $gas = $xml.CreateNode("element","analytics:GoogleAnalyticsService","clr-namespace:"+$project.Properties.Item("RootNamespace").Value+".Analytics") 
  $gas.SetAttribute("WebPropertyId", "UA-12345-6") 
  $lto.AppendChild($gas) 
  # save the App.xaml file 
  $xml.Save($localPath.Value) 
} 

Gravatar