Getting Next Month in MSBuild
I’m working on an application that needs to deploy a new database each month, in order to manage the large amount of data involved. We’d like to automate this as much as possible, and since we already have the database schema in a Visual Studio 2010 database project, we figured the simplest thing would be to create an MSBuild task to do this, and then schedule it as part of our build server. The format we want to use for the database name is simply “Databasename_201103” where the 201103 is next month’s year and month number (in this case, March 2011).
So, to make the first part work is pretty simple. You can pass in a parameter, TargetDatabase, to the Deploy task of a .dbproj file and it will create the new database as part of the deployment. You can do this from the command line, or directly in MSBuild, which looks something like this:
<Target Name="DeployLoggingDBNextMonth" DependsOnTargets="GetNextMonth">
<MSBuild Projects="src\Databasename.dbproj"
Targets="Build;Deploy"
Properties="TargetDatabase=Databasename_$(NextMonthString)" />
</Target>
That’s the easy part. The tough part is actually getting the string, 201103, to use in the $(NextMonthString) value.
My searching led me to the MSBuild Community Tasks Project, which includes things like Add and Time that I thought I could use. Time even has a nice Format property so you can format out the time. In fact, if all I wanted to know was *this* month’s code, it would be a simple matter:
<Target Name="ThisMonth">
<Time Format="yyyyMM">
<Output TaskParameter="FormattedTime" PropertyName="ThisMonth" />
</Time>
<Message Text="This Month: $(ThisMonth)" />
</Target>
Result:
This Month: 201102
Now of course, I could use the <Add> task from the community tasks project, and this would work *most* of the time:
<Target Name="GetNextMonthAdd" DependsOnTargets="ThisMonth">
<Add Numbers="1;$(ThisMonth)">
<Output TaskParameter="Result" PropertyName="Result" />
</Add>
<Message Text="Next Month Using Add: $(Result)" />
</Target>
Result:
Add numbers: 1 + 201102 = 201103
Next Month Using Add: 201103
Eureka! Ship it! That’s perfect! But wait, somehow years of unit-testing have me wondering, what will happen in December? Oh, right, it will yield this:
Add numbers: 1 + 201112 = 201113
Next Month Using Add: 201113
That’s not a problem, is it? Everybody knows January 2012 is the 13th month of 2011, right?
What about using a Conditional? We could always add 1, unless the month is 12, in which case we add 89 (because that’s intuitive). That would yield 89 + 201112 = 201201, which is what we expect. Sadly, apart from being an awful hack, I can’t see how to actually implement a <Conditional> <When> <Otherwise> block inside of an MSBuild task.
Fortunately, the MSBuild Community Tasks have a solution, which is to simply let me execute some arbitrary .NET code in a script-like fashion. In this case, one line of C# is easily able to do what many lines of declarative gunk can’t easily do. The end result (with some declarative gunk to wrap my one line of code) is shown here:
<PropertyGroup>
<GetNextMonthString>
<![CDATA[
public static string ScriptMain() {
return DateTime.Today.AddMonths(1).ToString("yyyyMM");
}
]]>
</GetNextMonthString>
</PropertyGroup>
<Target Name="GetNextMonth">
<Script Language="C#" Code="$(GetNextMonthString)">
<Output TaskParameter="ReturnValue"
PropertyName="NextMonthString" />
</Script>
</Target>