Skip to main content

Automatic Updating of AssemblyFileVersion in C# Projects

So my good buddy and co-worker Stacy Draper and I put together a solution today to handle versioning of .NET assemblies used for SharePoint web part development. Maybe this has been done before, maybe even better than this, but a quick Google search didn't turn up squat for us, or at least anything we liked, so we created our own solution.

The general problem revolves around the fact that the AssemblyVersion attribute in .NET assemblies is used by other assemblies when referencing strong named/signed assemblies stored in the GAC. Now you don't have to put your SharePoint assemblies in the GAC, but in most all cases, it simply makes the most sense. If you change the AssemblyVersion of an already installed and used over and over and over again web part, you're in a world of hurt if you arbitrarily change the AssemblyVersion to something new. At that point, you're faced with having to update 10's to 100's to 1000's of web part pages that reference the original web part. That's not to say there aren't times when it makes sense to do so, but for day to day development and deployment scenarios where it really isn't a new version of the web part, but itis an updated version of the code, you still want to be able to have a very noticeable way to see what version is currently installed on your portal.

So that takes us to what Stacy and I whipped up today. I created a script that updates your AssemblyInfo.cs file, setting the AssemblyFileVersion attribute to a time-stamped version number, based off of the AssemblyVersion attribute's value, of when the assembly was built. Stacy created a script that updates the feature.xml file that is part of the SharePoint feature with the new version number of the AssemblyFileVersion to be displayed within the description of the feature. This is so you can also see that version info from within SharPoint, in addition to viewing the file properties. Both of these scripts are then configured to run in your pre-build events for your C# project. What follows is my pre-build commands and the content of my VBS script. I place my VBS file in a folder named UTIL inside my C# project. See Stacy's blog for his stuff.

CD "$(ProjectDir)UTIL"
cscript /nologo UpdateAssemblyFileVersion.vbs "$(ProjectDir)Properties\AssemblyInfo.cs"

'--- UpdateAssemblyFileVersion.vbs
'--- Author - Jim Sally
'--- Date - 2007.10.23
'--- This updates the AssemblyFileVersion attribute in the AssemblyInfo.cs file
'--- of a given C# project. The AssemblyFileVersion is updated to contain the
'--- first two digits of the AssemblyVersion, with YMMDD.HHmm as the last two
'--- digits.
Option Explicit

Dim fileHeader
fileHeader = "UpdateAssemblyFileVersion.vbs :: "

EnsureAssemblyInfoFileExists WScript.Arguments(0)
UpdateAssemblyFileVersion WScript.Arguments(0)

Private Sub EnsureCommandLineArgumentExists()
If WScript.Arguments.Count <> 1 Then
WScript.Echo fileHeader & "You must supply the path to the AssemblyInfo.cs file as the only argument on the command line"
WScript.Quit 1
End If
End Sub

Private Sub EnsureAssemblyInfoFileExists(AssemblyInfoFileName)
Dim fso

Set fso = CreateObject("Scripting.FileSystemObject")
If Not fso.FileExists(AssemblyInfoFileName) Then
WScript.Echo fileHeader & "AssemblyInfo.cs does not exist at the path provided -> " & AssemblyInfoFileName
WScript.Quit 1
End If

Set fso = Nothing
End Sub

Private Sub UpdateAssemblyFileVersion(AssemblyInfoFileName)
Dim assemblyInfo, newAssemblyInfo

assemblyInfo = GetContentsOfAssemblyInfoFile(AssemblyInfoFileName)
newAssemblyInfo = UpdateAssemblyInfoText(assemblyInfo)

If newAssemblyInfo <> assemblyInfo Then
SaveContentsOfAssemblyInfoFile AssemblyInfoFileName, newAssemblyInfo
End If
End Sub

Private Function GetContentsOfAssemblyInfoFile(AssemblyInfoFileName)
Dim fso, file

Set fso = CreateObject("Scripting.FileSystemObject")
Set file = fso.OpenTextFile(AssemblyInfoFileName)
GetContentsOfAssemblyInfoFile = file.ReadAll()

Set file = Nothing
Set fso = Nothing
End Function

Private Sub SaveContentsOfAssemblyInfoFile(AssemblyInfoFileName, infoText)
Dim fso, file

Set fso = CreateObject("Scripting.FileSystemObject")
Set file = fso.CreateTextFile(AssemblyInfoFileName, true)

Set file = Nothing
Set fso = Nothing
End Sub

Private Function UpdateAssemblyInfoText(AssemblyInfoText)
Dim posAV, posAFV, posEolAV, posEolAFV, aNAV
Dim originalAssemblyVersionText, originalAssemblyFileVersionText, newAssemblyFileVersionText

posAV = InStr(AssemblyInfoText, "AssemblyVersion")
posAFV = InStr(AssemblyInfoText, "AssemblyFileVersion")

If Not IsNull(posAV) And Not IsNull(posAFV) And posAV > 0 And posAFV > 0 Then
posEolAV = InStr(posAV, AssemblyInfoText, "]")
posEolAFV = InStr(posAFV, AssemblyInfoText, "]")

originalAssemblyVersionText = Mid(AssemblyInfoText, posAV, posEolAV - posAV)
originalAssemblyFileVersionText = Mid(AssemblyInfoText, posAFV, posEolAFV - posAFV)

aNAV = Split(Split(originalAssemblyVersionText, """")(1), ".")
newAssemblyFileVersionText = "AssemblyFileVersion(""" & GetNewFileVersionNumber(aNAV) & """)"

If originalAssemblyFileVersionText <> newAssemblyFileVersionText Then
UpdateAssemblyInfoText = Replace(AssemblyInfoText, originalAssemblyFileVersionText, newAssemblyFileVersionText)

WScript.Echo fileHeader & "Updated attribute from '" & originalAssemblyFileVersionText & "' to '" & newAssemblyFileVersionText & "'"
WScript.Echo fileHeader & "AssemblyFileVersion attribute already '" & newAssemblyFileVersionText & "'"
End If
WScript.Echo fileHeader & "AssemblyVersion or AssemblyFileVersion attribute not found"
WScript.Quit 1
End If
End Function

Private Function GetNewFileVersionNumber(versionArray)
GetNewFileVersionNumber = versionArray(0) & "." & versionArray(1) & "." & GetVersionYMMDD() & "." & GetVersionHHmm()
End Function

Private Function GetVersionYMMDD()
Dim y, m, d

y = right(year(now()),1)
If y = 0 Then
y = 10
ElseIf y > 6 Then
m = (y - 6) * 12
y = 6
End If

m = Right("00" & (m + Month(now)), 2)
d = Right("00" & Day(now), 2)

GetVersionYMMDD = y & m & d
End Function

Private Function GetVersionHHmm()
GetVersionHHmm = Right("00" & Hour(Now()), 2) & Right("00" & Minute(Now()), 2)
End Function


Stacy said…
Here's my contribution,guid,2b6de457-bc27-42f9-94cc-6d48b89d25fb.aspx
This was certainly a fun!
BigJimInDC said…
Just as a quick follow up comment, I wanted to document our decision making process to use VBS files, as I'm sure everyone will have an opinion on this.

The list of options Stacy and I came up with included custom MSBuild Actions and PowerShell scripts. Our decision was based on VBS scripts requiring zero additional installations, along with extreme ease of changing the numbering scheme inside the script (for those of you that don't like what we came up with).

PowerShell would require a download and install if you don't already have it installed. And if you wanted to modify the custom Action, you'd have to rebuild and redeploy the assembly.

Popular posts from this blog

MS KB928365, ASP.NET Request.Headers.Add() Hack No Longer Works

So a project that I am currently a part of is using an ASP.NET 2.0 HttpModule to add some additional values to the incoming HTTP request's headers in the DEV environment (i.e., our local disconnected laptops) to simulate what an enterprise single-sign-on solution is performing in the production environment. It has worked like a charm. That is until I installed the new security update for the .NET Framework 2.0 release this past Wednesday, July 10, MS KB928365.

Apparently this "hack"has been disabled with the release of this security update.

When attempting to call Headers.Add(), with or without the above hack in place, you will now receive a PlatformNotSupported exception.

All in all, this post is by no means a rant against the security update, but simply an attempt to add a quick answer to the "Google answer machine" for those searching. I am also already aware of a number of other potentially better solutions than the one currently in place for simulating th…

Temporal Database Design, Soft Deletes, and Ayende's Razor

This is a formal follow-up to a post on Ayende's blog on "Avoiding Soft Delete's" in your database where I question the lack of temporal database solutions being applied to these types of problems.

After Oren claimed the following, I felt it necessary to expand on it in a blog post of my own, rather than continuing to clutter his comments, and hopefully finally bring some traffic to my own blog :-)
Ayende’s RazorThis is a response to a comment on another post:Oren, in all seriousness, I thought that problems that were "(a) complex, (b) hard to understand (c) hard to optimize" were the kinds that folks like you and I get paid to solve...Given two solutions the match the requirements of the problem, the simpler one is the better.I could just as well call that statement "Jim's Razor", as I believe in it as much as you do Oren, so no arguments there.

But in the same vane, "wise" (i.e., experienced) software architects/developers strategical…

It was bound to eventually happen...

...that I would start a blog.

Anyway, I've spent better than the past 5 years now reading other people's blogs, and steering clear of starting my own. I still don't think I have the time to offer a lot of content, but hopefully what I do post will be more useful than this obligatory intro. The only real reason I decided to start this was to give myself somewhere to post write-ups on random topics that I couldn't find an answer to via Google. Hopefully posting those write-ups here will save someone else some time some day.

In the mean time, you can keep yourself busy reading my Google Reader Shared Posts.