Convention over Configuration
Just a quick note on Convention over Configuration, I believe this is one of the more useful practices that you can apply in your codebase. So what is this all about then? Well think of about everything you do in life; think for example about “opening a door” or “turning on the water” we all exactly know how to do those things, and because of that it is a fast action. It is something that we don’t have to figure out just before doing it, again and again. These are conventions.
When we start applying these sorts of ideas to our code base this would mean that certain operations need far less thought as they will be the same each and every time. A good example here is mapping the controllers and view together and extracting the URL from the controller name in MVC. If you are a developer that knows these conventions then you know exactly where to look for the code of a certain URL. No need to figure this out each and every time.
Conventions also bring an opportunity to automate things within your system, because you are following certain rules in your code, you can write some other code that does something accordingly to these same rules. Configuration is a area that greatly benefits from conventions. Instead of having to manually configure how each different part works together you can write some code that configures this for you. You can also write some tests that verify your conventions.
Configure your Conventions
Many frameworks apply conventional thinking in their code, making it easier for their users to use it. There are frameworks like Ruby on Rails that rely very much on conventions, the only thing here is that these conventions cannot be changed very easily. This makes the framework very opinionated. This is not necessarily a bad thing infect this means that any Ruby on Rails developer can jump to any other Ruby on Rails project and start working on it (sort of).
But frameworks that allow you to configure your own conventions are much more powerful, because not all conventions make sense in every scenario. FubuMVC is one such framework where we try not to be to opinionated (we are) so that you may configure your own conventions.
Not just for framework code
I try to figure out what conventions that are in a system that I am working on and make them explicit as soon as it makes sense. The benefits are clearly there in my eyes, being able to test them and apply them is great. This is not something you would only want to apply in framework code, for example the win forms application that I created in my CQRS example also uses conventions to hook up the views with the presenter. Very easy and I don’t have to configure it manually ever again.
Now adding a new event to the application is like turning on the water or opening the door.
CQRS – Trying to make it re-usable
If you have been following the source code changes on GitHub you may have noticed that I renamed the folder Fohjin.DDD to Fohjin.DDD.Example, my intention is to not make anymore changes there. Instead I have created a new folder next to it and in there I am rebuilding the same components but now with re-use and ease-of-use in mind.
The first thing that is very obvious in the example code is that the domain is not very persistence ignorant, something that is valued a lot in Domain-Driven Design. So this is something that I wanted to try to address first. I really like the way NHibernate makes our code persistence ignorant and am attempting to solve this in a similar manner, using Castle DynamicProxy.
Since I am a complete Castle DynamicProxy noob I asked the help of Krzysztof Ko?mic who is an active committer on the Castle project. Krzysztof was gracias enough to get me through the basics in .Net proxy-ing. For those interested in this technology I would also recommend reading his excellent blog series about the same topic.
What I want to achieve is that you get back your poco and I am going to show you one suggestion that I have working now.
For comparison I’ll show you here how an aggregate root looks like in my initial example code (it is stripped down to only contain one behavioral method):
1 namespace Fohjin.DDD.Domain.Client
2 {
3 public class Client : BaseAggregateRoot<IDomainEvent>, IOrginator
4 {
5 private Address _address;
6
7 public Client()
8 {
9 registerEvents();
10 }
11
12 public void ClientMoved(Address newAddress)
13 {
14 Apply(new ClientMovedEvent(newAddress.Street, newAddress.StreetNumber, newAddress.PostalCode, newAddress.City));
15 }
16
17 IMemento IOrginator.CreateMemento()
18 {
19 return new ClientMemento(Id, Version, _address.Street, _address.StreetNumber, _address.PostalCode, _address.City);
20 }
21
22 void IOrginator.SetMemento(IMemento memento)
23 {
24 var clientMemento = (ClientMemento) memento;
25 Id = clientMemento.Id;
26 Version = clientMemento.Version;
27 _address = new Address(clientMemento.Street, clientMemento.StreetNumber, clientMemento.PostalCode, clientMemento.City);
28 }
29
30 private void registerEvents()
31 {
32 RegisterEvent<ClientMovedEvent>(onNewClientMoved);
33 }
34
35 private void onNewClientMoved(ClientMovedEvent clientMovedEvent)
36 {
37 _address = new Address(clientMovedEvent.Street, clientMovedEvent.StreetNumber, clientMovedEvent.PostalCode, clientMovedEvent.City);
38 }
39 }
40 }
And here is the same aggregate root but now _more_ persistence ignorant:
1 namespace Fohjin.DDD.Domain.Client
2 {
3 public class Client
4 {
5 protected virtual void Apply(object @event) { }
6 protected IEnumerable<Type> RegisteredEvents()
7 {
8 yield return typeof(ClientMovedEvent);
9 }
10
11 protected virtual Address Address { get; set; }
12
13 public void ClientMoved(Address newAddress)
14 {
15 Apply(new ClientMovedEvent(newAddress));
16 }
17 }
18 }
Ok this may seem a bit strange at first, I mean we are sending an event into nothing and we don’t set our state anymore. Let me try to explain
Convention over Configuration
Well in order to make this work I am going to insist on some conventions, in fact it is going to be very opinionated. If you want to learn more about convention over configuration then I would suggest reading Jeremy Millers article on MSDN. Personally I am all in favor for some strong guidelines so lets just go over them and see if they make sense to you as well.
Mandatory methods
The protected virtual method Apply and the protected method RegisteredEvents are both mandatory if you want to instantiate the aggregate root using this approach.
In the RegisteredEvents you return all the events that the aggregate root can publish, this is to let the persistence code know what it can expect. So when you add some new behavior that will publish a new event then you just add it to the list here. I think that you can sort of think about this as the mapping classes in NHibernate.
The Apply method is used to publish the events from the domain behavior, but as you can see it has an empty method body, so nothing will happen, right? Here we use Castle DynamicProxy to intercept calls to this method and replace the behavior (non-existing) with our persistence logic. Where this logic comes from I’ll explain in a minute.
Internal state needs to be protected virtual properties
One other difference with this approach versus the example code is that instead of private fields or private properties for the internal state, it now needs to be in the form of protected virtual properties. The reason for this is because state will not be managed by the aggregate root anymore, but instead by something that we add using Castle DynamicProxy and we will be using interception to map the properties to the internal representation of the state. The code even throws an exception if the aggregate root tries to set the internal state directly.
The event store
I haven’t done anything with the event store yet, I don’t think the functionality will change much from the code in the example project. One thing that will remain the same is the dependency on interfaces, and not on specific implementations. This is obviously because it is good design practices. And it will enable us to mix this new approach and the approach using the BaseAggregateRoot class together with the same event store. The BaseAggregateRoot is discussed in detail here.
Mix-in magic
So after reading the previous chapter you would probably say, but this aggregate root doesn’t implement anything? This is true, but when our repository is instantiating the aggregate root it is also adding or injecting two more classes into it. One class that implements the IEventProvider interface and an other class that implements the IOrginator interface. This is called mixing-in classes and by doing so the aggregate root will implement both these two interfaces and it will re-direct calls to it to the mix-in classes. So as far as our event store knows it just is a class that implements the needed interfaces.
Why have everything protected?
Well in order to be able to intercept members using Castle DynamicProxy these members need to be visible to the interceptor. The most private way that is available to us is protected, and the reason why I want this to be as private as possible is because this is not domain logic, and thus should not be visible to the users of the domain. For the same reason you may notice that for example the IEventProvider implementation has all the members implement the interface explicitly thus hiding them when the domain is used and only showing them when the domain is explicitly casted to the IEventProvider interface.
I have been thinking about providing a basic interface that dictates that the Apply and RegisterEvents methods needs to be present, but I like this approach better as from the outside these implementation details would not be known, but I could imagine being able to support both approaches.
Reflect only once
There is quit a bit of reflection going on to discover and the properties of both the domain events and the aggregate root internal state. But once this is done once the results will be cached and re-used for each sequential need. But I am sure this can improve quit a bit
Conventional limits
So what if I run into the limits of this conventional approach? Well the Event Store only cares about that the different interfaces are implemented, so you will be able to switch back to the BaseAggregateRoot solution at any time. This does mean that you will have to do some more coding, but you will have all the freedom to do what ever you need to do. And the different approaches can be used side by side.
One other limit is that if you instantiate the aggregate root yourself nothing will be intercepted and mixed-in so no state will change nor will the events be published.
Current state
Currently the code only supports very basic usages and there are some challenges ahead of it being truly useable. Think for example about other entities in the same aggregate managed by the aggregate root and collecting their events as well. Also think about adding and removing entities from a list which may need some configurable conventions so you can decide when something needs to be added or removed.
I am thinking about the ability to provide adapters to be able to handle certain state changes differently from the conventional way. One that I see a need for already is the ability to set the aggregate root Id, since this is not managed by the aggregate root but by the event provider mix-in.
Finally
Currently the functionality is very limited but I have tests passing proving that this is working
so what I would like is to get some feedback on this approach. And yes I realize that this is very opinionated, and will not fit everybody’s approach. Having said that, keep the suggestions coming anyway.
Critical Mistakes Freelancers Make
![]()
Seeing as we are all human (well, presumably whoever is reading this post anyway), we should recognize that mistakes happen. They even have that saying, “To err is human…,” which goes to show that it is not only commonplace for us to err once or twice: it is expected. But a method is behind this madness, because making mistakes is one of the major ways we learn. This is no different for freelancers.

Finding our way over these bumps in the road often gives us valuable insight to take away. It helps us develop techniques and methods that we can incorporate into our creative process. As freelancers, we have the benefit of access to an entire online community that is willing to share its experiences so that we can learn without having to make the same mistakes.
So in this post, we look at 10 critical mistakes freelancers make. Hopefully, if you haven’t already made one of these mistakes yourself, you can learn the lesson behind it.
Also consider our previous articles:
- Getting Clients: Approaching The Company
- Invoice Like A Pro: Examples And Best Practices
- How To Identify And Deal With Different Types Of Clients
- Freelance Contracts: Do’s And Don’ts
- Marketing Rules And Principles For Freelancers
They Don’t Use A Contract
One of the first things freelancers learn when contracting out their services to others is… to use a contract! Unfortunately, we often learn this lesson the hard way. For whatever reason, we think that a particular client of ours is someone we can work for without the aid and protection of a contract. This tends to end in one way: by biting us in the back end.
Without this safeguard in place, you open yourself up to so many potential problems, and you may inadvertently end up committing to more than you had intended or even imagined. Freelancers only make this mistake once, if at all. This lesson is not a secret in the freelance community. The advice comes up often: always use a contract. And many heed the warning once they hear it.
They Misuse Social Media (Or Don’t Use It At All)
Another common, but critical, pitfall that freelancers tumble into is misusing social media, if they even use it at all. Social media is a major tool that offers all freelancers an invaluable resource at their fingertips. An entire community of professionals connected via modems, ready and willing to offer each other whatever assistance they can. Neglecting this stream of industry insight, or not using it properly, can hinder the growth of your business.
Social media is about interacting with people and fostering relationships, which, if done with consideration and attention, can create opportunities you would have otherwise missed out on (not to mention friendships that can outlast jobs). Especially at the beginning of your freelancing career, if you make the mistake of misusing the media, you could be seen as an anti-social pariah in your corner of the Web.
They Put Quantity Over Quality In Their Portfolio

When putting their portfolio together, some freelancers mistakenly believe that the more they add to their portfolio, the better. Then it becomes about quantity and not quality of work. They forget the value of the portfolio in opening doors and creating opportunities.
The phrase “Put your best foot forward” applies in this situation. Your portfolio speaks volumes about your skills, freeing you from having to say too much and risk coming off as more arrogant than confident. Let your portfolio do the talking, and don’t make the mistake of prioritizing quantity and sending the wrong message. Quality makes the best first impression, so make the most of it.
They Stop Learning

This one has to be said. It can do so much harm to freelancers, no matter what their field: that is, they stop learning. But especially for freelancers who work in a field as dynamic and ever-expanding as design and development, staying ahead of the curve is absolutely crucial to meeting your clients’ needs.
This field is continually evolving with new techniques and applications. Throwing in the towel on education is virtual suicide. You, your work and your career would stagnate. Thankfully, with this online culture we have today, cultivating an environment in which we can sustain our education is easy. Not taking advantage of these learning opportunities is a mistake that could potentially cost you your business.
They Don’t Know How To Deal With Clients

Another common mistake is that freelancers forget their people skills when dealing with clients. For whatever reason, we let slip in our minds that clients hire us because they don’t know how to do the work themselves. They are in unknown territory, and as freelancers we should always be sensitive to that and bridge as many gaps in knowledge as we can. This will only improve your future dealings with the client and earn you more respect and trust in the business.
Obviously, without clients, you are a freelancer in title alone, so make sure you know not only how to engage clients but how to entice them back. Being able to assess needs that they aren’t even able to articulate and then communicating it all back to them is an invaluable skill. Neglecting it can be costly.
They Fail To Prepare For Dry Spells

This mistake is definitely better learned second-hand, and that is not preparing for occasions when no work is coming in. Droughts hit even the best of them, especially in these tough economic times. Freelancers often forget to account for that in their pricing structure and to save up in good times for when things go south.
There is a logic behind the rates we charge, and part of it is to sustain us after we have completed work for one client and eagerly await the next. Of course, we can always find work to do, but paying work is what sustains us as freelancers. Calling this mistake costly is too close to punning for comfort, but its impact is definitely felt and could force you to suspend freelancing and seek out supplemental employment, thus making it even harder for you to create your own opportunities.
They Overload Their Plate

This next mistake sometimes results from a fear of the aforementioned dry spell. Of course, greed might also play a role. Whatever the reason, some freelancers don’t know when enough is enough, and they continue to take on new projects as their plate overloads. Overextending yourself and your business like this can destabilize your workflow.
Freelancers need a certain degree of self-awareness to know when they have reached their limit. Reputation—that is, a good one—is important to your business’ development. Spreading yourself too thin is never good, and the distraction could hamper your creativity. This is another of those mistakes that are difficult to recover from.
They Miss A Deadline (And Think It’s No Big Deal)

This, too, is often a consequence of the previous mistake in our list. Falling behind when you are overloaded is all too easy, but missing a deadline can have a debilitating effect on your business. And if you think missing a deadline is no big deal, your career may be over before it begins. Deadlines keep you on track and help you multitask, as well as keep your client on track with the development of their project.
Once again, reputation is critical to building your brand and making your mark in the freelancing market. And a great way to ruin that reputation is by proving yourself unreliable. Stay productive and ahead of your tasks to avoid disrupting your client’s timetable. If you end up making this mistake, own up to it. Don’t offer excuses, simply propose a new timetable and continue working hard to meet it. But clearly acknowledge the problem you have created for your client. If you make this mistake once, you may not have an opportunity to make it again.
They Lack Confidence

Lacking confidence in themselves or in their work is another mistake that can plague freelancers, even beyond their business. Being your own worst critic and holding your work to a higher standard than that of others is natural (right?). But at a certain point, you are no longer critiquing so much as tearing down your work. Dismissing the talent and abilities that have carried you this far is misguided and will do nothing for your productivity.
Without confidence, making it as a freelancer will be extremely difficult. You’ll start taking useful and well-intended criticism bitterly, missing the person’s point and spiraling further into a pool of doubt and self-pity. Lack of confidence hinders your skills and the growth of your business. Clients will pick up on it quickly, because the freelancer is supposed to have a commanding role. Our responsibility is to guide the client to make effective decisions and win them over to our point of view; without confidence, this becomes unlikely. You’ll undervalue both yourself and your work. So have faith in your abilities, and know that your unique voice is needed in the ranks of the freelancing arena.
They Go To Work For Someone Else

Another blunder freelancers make is to work tirelessly to build their business, only to accept the first offer for a cushy job that comes along. No longer being your own boss would seem easy to adjust to, but it can be like moving back under your parents’ roof after you’ve tasted the freedom of living on your own. It simply doesn’t fit as comfortably as it once did. Simply readjusting is not so easy because freelancing is more than a job: it is a way of life.
Some people tell themselves that freelancing was all along a stopgap to some greater dream, but true freelancers find that pill hard to swallow. For some, that might be true, but then those people were not freelancers so much as temporary independent contractors. Freelancers crave the freedom that comes with the ’lancing. Still others believe they can work for someone else and maintain their freelancing on the side. In theory, this might appear viable. The reality is harsher: freelancing is full-time. It is a way of life, and turning it into a part-time job spells trouble.
Further Resources
Have a look at these related articles and resources:
- Three Big Mistakes That Can Make or Break Your Design Career
A discussion on Fuel Your Interface of three major mistakes that could potentially end a designer’s career. - Marketing Rules and Principles for Freelancers
Freelance Folder offers some fantastic marketing insight that can help guide you in your freelance career. - To Err Is Human: How to Handle Freelance Mistakes
Freelance Sprout looks at how to handle mistakes as a freelancer. - Top Ten Mistakes That Aspiring Professional Freelance Photographers Make
Paul Burwell offers some helpful advice for photographers who long to branch into the freelancing market. - Freelancing FUBARs: 8 Common Mistakes
Freelance Adviser looks at common mistakes made by freelancers when they are just starting out.
(al)
© Robert Bowen for Smashing Magazine, 2009. | Permalink | 8 comments | Add to del.icio.us | Digg this | Stumble on StumbleUpon! | Tweet it! | Submit to Reddit | Forum Smashing Magazine
Post tags: freelancing
Adding Images on Silverlight 4 using drag and drop and the web camera.
Silverlight 4 brings a few features that I could use since version 2, the biggest improvement in my case is Drag and Drop support, I really see many different use cases for that particular feature. I created a little test to see how it works and how much code is required to use. The use case is simple, drag and drop an image into a map and added into the graphic layer at the position dropped. For the test I’ll use the ArcGIS API for Microsoft Silverlight™/WPF™ version 1.1 that you can download here.
I need to wire the map control to allow dropping, even if I reference dlls compiled in Silverlight 3 the controls now will have the property AllowDrop to set it to true or false and the event Drop to catch when a object gets drop on top of the control. Also in the contructor we are going to initialize the CaptureSource for our web camera.
public MainPage() { InitializeComponent(); MyMap.AllowDrop = true; MyMap.Drop += new DragEventHandler(MyMap_Drop); _captureSource = new CaptureSource(); _draw = new Draw(MyMap); _draw.DrawComplete += new EventHandler<DrawEventArgs>(_draw_DrawComplete); }
The drop event will receive the data dropped on the object, you can received more than one object at the time, in this sample I only care about the first file to added it into a graphic layer on top of the map. I’ll get the point and I’ll read the file drop to add it as an image. Make sure you only drop images in this case. The drag and drop control argument also comes with the method to get the position where the object was dropped.
void MyMap_Drop(object sender, DragEventArgs e) { IDataObject mydata = e.Data; FileInfo[] fileInfo = (mydata.GetData(mydata.GetFormats()[0])) as FileInfo[]; if (MyMap.Extent != null) { System.Windows.Point screenPoint = e.GetPosition(MyMap); ESRI.ArcGIS.Client.Geometry.MapPoint mapPoint = MyMap.ScreenToMap(screenPoint); GraphicsLayer myGraphicLayer = MyMap.Layers["myGraphicLayer"] as GraphicsLayer; BitmapImage source = new BitmapImage(); source.SetSource(fileInfo[0].OpenRead()); Graphic grap = new Graphic() { Geometry = mapPoint, Symbol = new ESRI.ArcGIS.Client.Symbols.PictureMarkerSymbol() { Source = source, Width = 20, Height = 20 } }; myGraphicLayer.Graphics.Add(grap); } }
Also you can take a picture and added into the map, we created above. In this case we add a polygon and will fill it with the picture we took using the web camera connected to the local computer.
private void Button_Click_3(object sender, RoutedEventArgs e)
{
if (_captureSource.State == CaptureState.Started)
{
ESRI.ArcGIS.Client.Geometry.Polygon poly = new ESRI.ArcGIS.Client.Geometry.Polygon();
GraphicsLayer myGraphicLayer = MyMap.Layers["myGraphicLayer"] as GraphicsLayer;
_captureSource.Stop();
// create the brush
VideoBrush vidBrush = new VideoBrush();
vidBrush.SetSource(_captureSource);
Graphic gr = new Graphic()
{
Geometry = poly,
Symbol = new ESRI.ArcGIS.Client.Symbols.SimpleFillSymbol()
{
Fill = vidBrush
}
};
myGraphicLayer.Graphics.Add(gr);
ButtonPic.Visibility = System.Windows.Visibility.Collapsed;
}
else
{
// request user permission and display the capture
if (CaptureDeviceConfiguration.AllowedDeviceAccess || CaptureDeviceConfiguration.RequestDeviceAccess())
{
ButtonPic.Visibility = System.Windows.Visibility.Visible;
_captureSource.Start();
}
}
}
Without any doubt Silverlight 4 has features that are going to make developers simplify their use cases and more flexibility in writing the code, in a few simple lines of code I was able to add images on top of a control as well as an image coming from the web camera into a created object. I for one, looking forward to the release of Silverlight 4, this is the version we have been waiting for.
Need the XAML for the Map and Graphics Layer?
<esri:Map x:Name="MyMap"> <esri:Map.Layers> <esri:ArcGISTiledMapServiceLayer ID="basemap" Url="http://services.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer" /> <esri:GraphicsLayer ID="myGraphicLayer" /> </esri:Map.Layers> </esri:Map>
Read my Silverlight 4 related posts
Cheers
Al
Follow me in twitter | bookmark me | Subscribe to my feed | Add stats to your blog
None of my opinions reflects opinions or views by my employer nor Microsoft. This is just my personal blog that is provided "AS IS" with no warranties and confer no rights. The postings on this site are my own and don’t necessarily represent ESRI’s positions, strategies or opinions.
SaaS & Business Intelligence at Dreamforce
Database Change Management with Tarantino
Seeing as how I have not graced these pages with my presence in quite some time. I figured I better get something going. A little while back my team started working with some outside contractors and our make shift way of keeping changes in sync and under control was simply not cutting the mustard.
Why do we need DB Change management
Okay then Mr. Developer (or Mrs.), Why do we need source control? We need database change management for the same reason we need source control. To help manage the code and dependencies our applications rely on.
Recipes for Disaster:
Team of developers all working/developing off of a central database
Team of developers working/developing off of local database, with no way to keep changes in sync.
So what’s the problem with these recipes? As new features are developed for an application it is very likely that the database will need schema changes to support these features. These could be as simple as new tables or adding a column. If we have schema changes, we have to make sure that the code base for the app is deployed concurrently with the schema changes.
When deploying software to production, code files and libraries can usually be overwritten with a new version. Databases however must be updated intelligently.
So we’ve got a problem, do we have a solution?
Enter Tarantino. I was talking with Jarod F. and he said he had heard some stuff about it, but had never used it so I started digging around and found enough pieces to get it working. So first thing I did was head over to the Tarantino project site. Starting reading around and then i did a little googling and managed to dig up enough information to really wet my appetite and to get it working. Then Eric Hexter was kind enough to give me some time via IM and cleared up several areas for me.
Before I get into the specifics of how to set all this up, I want to explain how Tarantino finds and keeps changes in sync.
- Tarantino is a forward only change management system, you can’t roll back.
- If you use Tarantino, you can’t go make changes by hand manually
- Tarantino uses RedGate SQL Compare if you want, to compare DB Schemas
- Tarantino makes a table in every DB it touches to keep track of what scripts have been executed on that DB
The system is based on a set of conventions which allow incremental changes to the database schema. The conventions are to create two subdirectories in your database scripts folder. Create– this is where your inital schema change scripts go. Update – this is where your change scripts should be placed. They should be named with the following convention ####SCRIPTNAME.sql where #### is the script number with leading zeros. This will ensure the first script 0001_first_schema_change.sql would be executed first. There is a third folder, ExistingSchema, that you will need if you are going to start using Tarantino on an app that is already in development or production.
There are two ways to use Tarantino, command line, and as a NAnt task. I will be showing the NAnt task method, as it has the most examples available on the net.
Project Setup
So what I have done, is created a very simple project. A class library with a Fluent NHibernate configuration, a test fixture to create the database schema, and two simple entities which I have listed below:
public class Order
{
public virtual int Id { get; set; }
public virtual int OrderNumber { get; set; }
public virtual string PONumber { get; set; }
public virtual IList<OrderItem> OrderItems { get; set;}
public Order()
{
OrderItems = new List<OrderItem>();
}
}
public class OrderItem
{
public virtual int Id { get; set; }
public string ItemName { get; set; }
public string Description { get; set; }
}
and my NHibernate and Test Fixture setup:
[TestFixture]
public class DB_Setup_Fixture
{
private Configuration _configuration;
[TestFixtureSetUp]
public void FixtureSetup()
{
_configuration = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(c =>
c.FromConnectionStringWithKey("testData"))
.UseReflectionOptimizer()
.ShowSql())
.Mappings(m =>
m.AutoMappings.Add(AutoMap.AssemblyOf<Order>()
.Where(x => x.Namespace.EndsWith("Domain"))))
.BuildConfiguration();
}
[Test, Explicit, Category("DBSetup")]
public void Create_DB_Schema()
{
var exporter = new SchemaExport(_configuration);
exporter.Create(true, true);
}
}
The one thing to make note of here, is that my test is set to explicit, and I gave it a category of “DBSetup”. This will be important once we start configuring NAnt and Tarantino.
So now we have a very simple application that we can use to play around with Tarantino. To get Tarantino working, I am using the modified NAnt files that CodeCampServer is using. I just copied over their NAnt folder they have checked into source control. There are two NAnt build files that are required to use Tarantino. Disclaimer: I am a total NAnt noob, this is not my usual build utility, but I figured out enough to make it work.
Common.build
<?xml version="1.0" encoding="utf-8"?>
<project xmlns="http://nant.sf.net/schemas/nant.xsd">
<!-- Database change management -->
<target name="rebuildDatabase" depends="dropDatabase, createDatabase" />
<target name="updateDatabase">
<property name="action" value="Update" />
<call target="manageSqlDatabase" />
</target>
<target name="createDatabase">
<property name="action" value="Create" />
<call target="manageSqlDatabase" />
</target>
<target name="dropDatabase">
<property name="action" value="Drop" />
<call target="manageSqlDatabase" failonerror="false"/>
</target>
<target name="manageSqlDatabase">
<manageSqlDatabase
scriptDirectory="${database.script.directory}"
action="${action}"
server="${database.server}"
integratedAuthentication="${database.integrated}"
database="${database.name}"
username="${database.username}"
password="${database.password}"
/>
<if test="${action != 'Drop'}">
<echo message="Current Database Version: ${usdDatabaseVersion}" />
</if>
</target>
<script language="C#" prefix="migration" >
<references>
<include name="System.IO.dll" />
</references>
<code>
<![CDATA[
[Function("next-migration-number")]
public static string NextMigration(string path ) {
string[] files = System.IO.Directory.GetFiles(path);
int count=1;
if(files.Length > 0)
{
string filename = System.IO.Path.GetFileName(files[files.Length-1]);
try
{
count = Convert.ToInt32(filename.Substring(0, 4));
count++;
if(count%2 == 0)
count++;
}
catch
{
}
}
return string.Format("{0:0000}", count);
}
]]>
</code>
</script>
</project>
This file sets up most of the plumbing that hooks up the Tarantino NAnt targets for use in the nant.build file
NAnt.build
<?xml version="1.0" encoding="utf-8"?>
<!--EXTERNAL_PROPERTIES: usdDatabaseVersion, CCNetLabel-->
<project name="TarantinoSample" xmlns="http://nant.sf.net/schemas/nant.xsd">
<!-- ************** -->
<!-- Initialization -->
<!-- ************** -->
<include buildfile="common.build"/>
<loadtasks assembly="lib\nant\Tarantino.Nant.Tasks.dll" />
<!-- ***************** -->
<!-- Master Properties -->
<!-- ***************** -->
<property name="company.name" value="Tarantino Test"/>
<property name="assembly.unittests" value="TarantinoSample.dll"/>
<!-- Version settings -->
<property name="project.config" value="debug"/>
<!-- Folder references -->
<property name="dir.solution" value="src"/>
<property name="dir.build" value="build" dynamic="true"/>
<!-- Compilation settings -->
<property name="nant.settings.currentframework" value="net-3.5" />
<property name="file.solution" value="${dir.solution}/${project::get-name()}.sln"/>
<!-- Database migration settings -->
<property name="database.script.directory" value="dbChangeScripts" />
<property name="database.server" value="localhost" overwrite="false"/>
<property name="database.name" value="OrderData" overwrite="false"/>
<property name="database.integrated" value="true" overwrite="false" />
<property name="database.username" value="dbuser" overwrite="false"/>
<property name="database.password" value="P@ssword1" overwrite="false"/>
<!-- ********************************** -->
<!-- Database Change Management Targets -->
<!-- ********************************** -->
<target name="reset-database" depends="dropDatabase, createDatabase">
<nunit2 failonerror="true" verbose="true">
<formatter type="Plain" />
<test assemblyname="${dir.solution}/bin/${project.config}/${assembly.unittests}">
<categories>
<include name="DBSetup"/>
<exclude name="*"/>
</categories>
</test>
</nunit2>
</target>
<target name="db-migration">
<delete file="${database.script.directory}/Update/_New_Script.sql" />
<call target="reset-database" />
<call target="create-versioned-database" />
<property name="migrationScriptName" value="${migration::next-migration-number(database.script.directory+'/Update')}_AutoGeneratedMigration.sql"/>
<if test="${file::exists('c:\program files (x86)\red gate\SQL Compare 8\SQLCompare.exe')}" >
<property name="redgate.exe" value="c:\program files (x86)\red gate\SQL Compare 8\SQLCompare.exe"/>
</if>
<if test="${file::exists('c:\program files\red gate\SQL Compare 8\SQLCompare.exe')}" >
<property name="redgate.exe" value="c:\program files\red gate\SQL Compare 8\SQLCompare.exe"/>
</if>
<exec
program="${redgate.exe}"
commandline="/f /v /server1:${database.server} /server2:${database.server} /database1:${database.name} /database2:${database.name}Versioned /scriptfile:${database.script.directory}/Update/_New_Script.sql /exclude:Table:usd_AppliedDatabaseScript"
resultproperty="execReturnCode"
failonerror ="false"/>
<fail if="${execReturnCode != '0' and execReturnCode != '63'}" message="Redgate Compare Failed!"/>
<if test="${file::exists(database.script.directory + '/Update/_New_Script.sql') == false}">
<echo message=""/>
<echo message="---------------------"/>
<echo message="No Migration Required"/>
</if>
<if test="${file::exists(database.script.directory + '/Update/_New_Script.sql')}">
<move file="${database.script.directory}/Update/_New_Script.sql" tofile="${database.script.directory}/Update/${migrationScriptName}"/>
<echo message="Created Migration File ${database.script.directory}/Update/${migrationScriptName}"/>
</if>
</target>
<target name="create-versioned-database">
<tstamp></tstamp>
<manageSqlDatabase
action="Drop"
server="${database.server}"
integratedAuthentication="true"
database="${database.name}Versioned"
username=""
password="" />
<manageSqlDatabase
scriptDirectory="${database.script.directory}"
action="Create"
server="${database.server}"
integratedAuthentication="true"
database="${database.name}Versioned"
username=""
password="" />
</target>
</project>
Inside this file there are a few pieces that need to be configured for use in your own app, these are all listed under “Master Properties”
Change script creation
So without going into too many details, what I have done is created my initial schema using my Unit Test. From here I could script this out to a text file, but what I did was use SQL Compare, comparing my initial schema to an empty database. This gave me my initial SQL script that creates my database.
Just to verify that my initial schema creation script is good, I will have Tarantino create the Versioned database for me using this command:
lib\nant\nant.exe –buildfile:nant.build create-versioned-database
Tarantino then looks in the ExistingSchema folder, runs any scripts found there, then does the same thing for the Updates folder.
In order to test out the change management features, I made a small change to my domain, adding a decimal of cost to the OrderItem class should do the trick. Rebuild my solution, then back to the command line to run my “Create Database Migration Script.bat” file. This file is just running the NAnt task.
What happens then, is NAnt goes out and runs my DBSetup test that is in my project. Which recreates the “OrderData” database. Tarantino then creates the “Versioned” database, then uses SQL Compare to find the changes between the two, and creates a file in the update folder for me named, 0001_AutoGeneratedMigration.sql NOTE: It is important to rename this file before it is committed to source control, or before you apply these changes to a database. Tarantino tracks the scripts that have been run by filename.
Push to Production or Dev
So I have mentioned two databases so far in this post, OrderData and OrderDataVersioned. These two databases contain no data, that’s not to say that they couldn’t, if you had scripts that put it in. But that is not what they were designed to do. Our app.config is pointing to OrderData, so when we ask NHibernate to build the schema, it drops all the tables and recreates them, losing all data. OrderDataVersioned, is Tarantinos database that is used to keep changes in sync. In our development environment, every dev has a third database, we would call OrderDataProd for instance. This database could have sample data and other information in it. Let’s say, we don’t want to lose our data in this DB so what we do is let Tarantino handle the first two databases, then we deploy our schema changes to our development database. We use this same technique to push changes to production, after backups of course.
We can do this easily with the following command:
lib\nant\nant.exe –buildfile:nant.build updateDatabase –D:database.name=OrderDataProd
Now when I tried to run this, my database OrderDataProd did not exist, so I went to SQL manager, created it, and then ran the command again, only to be slammed with another error. Since the database existed, Tarantino did not run the create schema script, only the update, which created another error. The next command will create the database I want, by executing the schema creation script, then applying any updates to it.
lib\nant\nant.exe –buildfile:nant.build createDatabase –D:database.name=OrderDataProd
Bingo! I now have my 3rd database that I can dump data into and start developing on top of. Just to be sure that we can create another change script, and deploy it to this database, I will modify the domain again, by adding a quantity to the OrderItem object. I will leave this step up to you to try out.
Source Code
I know the blog formatting sometimes jacks up code samples a lot so I am making all the source for this post available two ways:
Information Sources
Eric Hexter – Huge Help
Palermo and Hexter Screencast on Tarantino

