This is a step-by-step guide to setting up Fluent Migrator to run on a build server using the MSBUILD project
Step 1: Setting up the migrations project
Create the Project
The migrations project is just a class library with a couple of NuGet packages added to it.
To make it easier later on to pick up the assembly from the MSBUILD project, we are not going to have debug/release bin directories in the same way other projects to. We will have one bin folder where the built assembly will be placed, regardless of build configuration.
To do that:
- Open up the properties for the project (either right-click and select “Properties”, or select the project then press Alt+Enter).
- Then go to the Build tab.
- Then change the Configurations drop down to “All Configurations”.
- Finally, change the output path to “bin\”
Add the NuGet Packages
The NuGet packages you want are:
- FluentMigrator – This is the core of Fluent Migrator and contains everything to create database migrations
- FluentMigrator Tools – This contains various runners and so on.
The Fluent Migrator Tools is a bit of an odd package. It installs the tools in the packages folder of your solution but does not add anything to your project.
Add the MSBuild tools to the project
As I mentioned the Fluent Migrator Tools package won’t add anything to the project. You have to manually do that yourself. I created a post build step to copy the relevant DLL across from the packages folder to the bin directory of the migrations project.
- Open the project properties again
- Go to the “Build Events” tab
- Add the following to the post-build event command line box:
xcopy "$(SolutionDir)packages\FluentMigrator.Tools.188.8.131.52\tools\AnyCPU\40" "$(TargetDir)" /y /f /s/v
NOTE: You may have to modify the folder depending on the version of the Fluent Migrator Tools you have
Add the MSBUILD project to the project
OK, so that sound a bit circular. Your migrations project is a C# project (csproj) and the Build Server will need an MSBUILD script to get going with, which will sit inside your C# project.
Since there is no easy way to add an MSBUILD file to an existing project, I found the easiest way was to add an XML file, then rename it to migrations.proj
Step 2: Configuring the MSBUILD Script
This is what the MSBUILD script looks like.
<?xml version="1.0" encoding="utf-8"?> <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <!-- Set up the MSBUILD script to use tasks defined in FluentMigrator.MSBuild.dll --> <UsingTask TaskName="FluentMigrator.MSBuild.Migrate" AssemblyFile="$(OutputPath)FluentMigrator.MSBuild.dll"/> <!-- Set this to the parent project. The C# project this is contained within. --> <Import Project="$(MSBuildProjectDirectory)\My.DatabaseMigrations.csproj" /> <!-- Each of these target a different environment. Set the properties to the relevant information for the datbase in that environment. It is one of these targets that will be specified on the build server to run. Other properties may be passed into the MSBUILD process externally. --> <Target Name="MigrateLocal"> <Message Text="Migrating the Local Database"/> <MSBuild Projects="$(MSBuildProjectFile)" Targets="Migrate" Properties="server=localhost;database=my-database" /> </Target> <Target Name="MigrateUAT"> <Message Text="INFO: Migrating the UAT Database"/> <MSBuild Projects="$(MSBuildProjectFile)" Targets="Migrate" Properties="server=uat-db;database=my-database" /> </Target> <!-- * This is the bit that does all the work. It defaults some of the properties in case they were not passed in. * Writes some messages to the output to tell the world what it is doing. * Finally it performs the migration. It also writes to an output file the script it used to perform the migration. --> <Target Name="Migrate"> <CreateProperty Value="False" Condition="'$(TrustedConnection)'==''"> <Output TaskParameter="Value" PropertyName="TrustedConnection"/> </CreateProperty> <CreateProperty Value="" Condition="'$(User)'==''"> <Output TaskParameter="Value" PropertyName="User"/> </CreateProperty> <CreateProperty Value="" Condition="'$(Password)'==''"> <Output TaskParameter="Value" PropertyName="Password"/> </CreateProperty> <CreateProperty Value="False" Condition="'$(DryRun)'==''"> <Output TaskParameter="Value" PropertyName="DryRun"/> </CreateProperty> <Message Text="INFO: Project is «$(MSBuildProjectDirectory)\My.DatabaseMigrations.csproj»" /> <Message Text="INFO: Output path is «$(OutputPath)»"/> <Message Text="INFO: Target is «$(OutputPath)\$(AssemblyName).dll»"/> <Message Text="INFO: Output script copied to «$(OutputPath)\script\generated.sql»"/> <Message Text="INFO: Dry Run mode is «$(DryRun)»"/> <Message Text="INFO: Server is «$(server)»"/> <Message Text="INFO: Database is «$(database)»"/> <MakeDir Directories="$(OutputPath)\script"/> <Migrate Database="sqlserver2012" Connection="Data Source=$(server);Database=$(database);Trusted_Connection=$(TrustedConnection);User Id=$(User);Password=$(Password);Connection Timeout=30;" Target="$(OutputPath)\$(AssemblyName).dll" Output="True" Verbose="True" Nested="True" Task="migrate:up" PreviewOnly="$(DryRun)" OutputFilename="$(OutputPath)\script\generated.sql" /> </Target> </Project>
Step 3 : Configuring the Build Server
In this example, I’m using TeamCity.
You can add a build step after building the solution to run the migration. The settings will look something like this:
The important bits that the “Build file path” which points to the MSBUILD file we created above, the Targets which indicate which target to run, and the “Command Lime Parameters” which passes properties to MSBUILD that were not included in the file itself. For example, the user name and password are not included in the file as that could present a security risk, so the build server passes this information in.
What about running it ad-hoc on your local machine?
Yes, this is also possible.
Because, above, we copied all the tools to the bin directory in the post-build step, there is a Migrate.exe file in your bin directory. That takes some command line parameters that you can use to run the migrations locally without MSBUILD.
- Open up the project properties again for your migrations C# project
- Go to the “Debug” tab
- In “Start Action” select “Start external program” and enter “.\Migrate.exe”
- In Command line arguments enter something like the following:
--conn "Server=localhost;Database=my-database;Trusted_Connection=True;Encrypt=True;Connection Timeout=30;" --provider sqlserver2012 --assembly "My.DatabaseMigrations.dll" --task migrate --output --outputFilename src\migrated.sql