Today one of the applications I was working on reached beta level. The project itself is an MVC 3 web application called MyRA, but I’d built a base library that handled lots of things from authentication and authorization, caching, data access, HTML helpers, client-side scripting, etc. The key words in that sentence are lots of things. Putting everything in one core library meant that developers were forced to either use the entire library or none of it.
As a result I split up the project into several projects: Core, Security, Data Access, WebApp, etc, updated the WebApp to use dependency injection with Ninject throughout and changed the project structure to make it easier to work with.
Project Structure and Subversion
Yes I am still using subversion until our GitHub Enterprise installation becomes available for serious use. But it already got me thinking about making project structure simpler. Plus, I’ve been tinkering with Continuous Integration lately and it made me realize that our current structure was going to make some things harder than necessary. So far our projects had used the repository layout described in the red bean book, which meant that every project had its own trunk, branches and tags:
However, most of the time, in our environment, a project’s dependencies are used only by that project, and this became very cumbersome, so when I restructured the project, I chose a one branch-per-solution-version approach, which looks like this:
This actually makes things a lot easier, even if Project1 is shared across other solutions, because it makes the provenance of the project clear and developers understand that a branch of a project is meant to work against components in the same branch. That makes it difficult to try and compile version 1.0 of one project against a dependency meant for version 1.1, which, if the build succeeded, could cause subtle bugs to appear at runtime!
NuGet Package Restore
When I’d finished, the project wouldn’t build, because as part of the restructuring, I got rid of all 3rd party libraries that I’d added via NuGet. But as I now had a per-solution hierarchy, it made sense to not check-in the NuGet dependencies anymore and just have them be installed as needed. Enter NuGet Package Restore, which makes this as easy as right-click on solution, then click:
Once I’d done this, checking out and building a project becomes the following sequence: 1) right-click > TortoiseSVN > svn checkout. 2) Double click solution file (.sln) 3) Hit F5. That’s it. And since you can use NuGet to setup an internal repository, this takes care of internal dependency management. I can easily translate this into a continuous integration workflow in TeamCity.
Many developers don’t know that the project files that Visual Studio creates (the .csproj files) are actually build scripts just like Apache Ant or Nant. They are actually really easy to use, but if you want to get a quick start on it, I’d recommend watching the Introduction to MSBuild on PluralSight.
Once you’ve gotten past the fear of mucking with sacred files like .csproj (when I started out with .Net I was scared to touch these things), you’ll see that there’s a lot you can do with them. One underused (in my opinion) feature is the Build Events tab in project properties:
This came in handy today. You see, this app uses a bunch of Oracle libraries, which tend to crash if they aren’t linked and run against the specific version and type of Oracle client. In other words, if you build against Instant Client and run against Server, it won’t work. To solve this stupid issue, we bundle all the necessary Oracle native DLLs along with the Web App, which works. However, these Oracle libraries are not in my startup project. They are inside a folder in a dependent project. I need to copy over these files whenever the project is built, like this:
(Yes, the folder is called oral for Oracle Libraries. Try and focus ) The project on the right is my startup project that references the project on the left, but because these DLLs aren’t managed libraries, I can’t link to them in the project. They are just expected to be in the bin folder at runtime.
Visual Studio 2010 doesn’t handle this. If you select the DLLs and set their build action, to Copy, you’ll get this in your output folder:
See that extra oral folder? That ensures that the DLLs don’t get picked up at runtime. Visual Studio will do this even if the .csproj file doesn’t ask for it:
The way to fix this is to add some post-build event commands. Since I could count on Visual Studio copying the DLLs along with the oral folder, all I need to do is:
- Move the DLLs from bin\oral to bin\
- Delete the bin\oral directory.
I need this to happen correctly no matter what build configuration is used, which means using the in-built macros in Visual Studio. This is what I ended up with:
REM The Oracle libraries in MyRA.DAL.Oracle\oral are copied over
REM along with the 'oral' folder, thus causing the DLLs to be not
REM picked up by the project. Move them to the right place.
move /y $(TargetDir)oral\*.dll $(TargetDir)
REM Then delete the 'oral' folder because we don't need it.
rmdir /q $(TargetDir)oral
Those $(TargetDir) symbols will be replaced with the absolute path to your project’s output directory. There are other macros that you can get to on the Build Events tab. Here’s what that editor looks like:
All this ultimately makes it into your project file, which when committed to source control, ensures that other developers get the same build that you do:
It’s really as simple as that.