Monday, March 9, 2009

Castle: The .Net framework of choice

Post 1 in a 101 series on the Castle IOC project called Windsor

I have long been an advocate of the Castle stack, stemming from my days at Change Corp. At the time we were using Spring.Net for our IoC container but also were using NHibernate. I noticed that we had castle DLL's in the bin and realised that NHibernate was using them for object creation. I looked into the Castle project and despite is lack lustre documentation was able to get IoC working pretty quickly. One of the things I personally like is that you did not NEED to use XML for you container registration (i.e.  defining which concrete type to use for which abstract request). On top of this the Monorail  project (the original MCV framework for ASP.Net) and Active Record The AR pattern sitting on top of NH) struck a chord as being particularly handy tools.

Years later the stack remains pretty much the same. There are some off shoot developments but by and large the same stuff I went to castle for is why I still like to use it.... well at least I thought.

Yesterday when writing the DI blog post I realised I had not directly used Castle in over a year. I have a wrapper that I use to abstract my interaction with the container and the OSS libraries that I use/play with hide the details too (MassTransit, SutekiShop etc). So I have decide to give a crash course for myself and the rest of the world on how to set up and exploit some of the more basic but super handy Castle features.

Castle, IoC And You: The set up

First and foremost you need to download the latest castle libraries. Currently these are RC3 and last updated in sep 07. Get them here.

Install the binaries, these will be jammed in the GAC for safe keeping. You can also use the straight DLLs if you need to for source control etc, just make to reference everything you need (i.e. secondary dependencies)

Right, with that sorted we are able to do the world silliest IOC demo.

Create a console application and include references to  Castle.Core.dll, Castle.MicroKernel.dll, Castle.Windsor.dll & Castle.DynamicProxy.dll.

I have create a very basic interface called IWriter which has one method: void Write()

I have two instances that implement it: HelloWriter and GdayWriter. It is probably a good time to note that in the castle world a "service" is generally referring to the interface or abstract type that you are calling and the "component" is the concrete implementations. So the IWriter would be our service and the HelloWriter and GdayWriter are considered components.

interface IWriter
{
void Write();
}
class HelloWriter : IWriter
{
public void Write()
{
Console.WriteLine("Hello World");
}
}
class GdayWriter : IWriter
{
public void Write()
{
Console.WriteLine("G'day world!");
}
}

Right, there are not the most useful of classes but they will help show you how to use Castle to control creation of dependencies next. In our Program class place the following:


using System;
using Castle.Windsor;

namespace CastleIocConsole
{
class Program
{
static void Main(string[] args)
{
var container = new WindsorContainer();
container.AddComponent<IWriter, HelloWriter>();
var writer = container.Resolve<IWriter>();
writer.Write();
Console.ReadKey();
}
}
}


Hit F5 to debug and see that


Hello world 

was output as the HelloWorld class was resolved and its implementation of Write was called on the second to last line before Console.ReadKey().


Next up we show how to get Named instances of concrete types. Say for example you have a default type, but in the odd occasion you need a different type; well instead of breaking the notion of DI and using concrete dependencies, you can call for implementation by a key. In this example we register 2 concrete types to the same interface, however one has a key specified.


static void Main(string[] args) 
{
var container = new WindsorContainer();
container.AddComponent<IWriter, HelloWriter>();
container.AddComponent<IWriter, GdayWriter>("gday");
var writer1 = container.Resolve<IWriter>();
var writer2 = container.Resolve<IWriter>("gday");
Console.Write("writer1 output: ");
writer1.Write();
Console.Write("writer2 output: ");
writer2.Write();
Console.ReadKey();
}

The output is:


writer1 output: Hello world 
writer2 output: G'day world!

I find that most of the time I expect a default implementation so do not explicitly set a key at registration time, but its nice to know its there when you need it.


Next we manage LifeStyle...

No comments: