Wednesday, April 25, 2007
11_factories.png
So most all of us are familiar with factories, and at first glance it seems an IoC container removes the need for having to write our own... but this isn't quite the case - for the container to be able create a component it needs:
  • A publicly accessible constructor.
  • All dependencies to be registered in the container.
  • Mechanisms to allow for configuration and dependencies to be injected via constructors, or properties.
Which is fine for classes you own, but what about those that you don't - well you have a few options, but one of the easy options is to create a factory and get the container to use the factory for creating instances - to do this we'll be using a facility.

Facilities are "add-ins" for the container, they can be registered in the container, and augment what the container is capable of doing - there are a lot of facilities for the container, and many of them are documented (to varying degrees) on the castle project wiki ... we will be using a couple of different facilities in this part, first off will be the factory support facility.

So let's start off with a class that's supplied to us by a 3rd party... I'm just going to make one as an example, to keep things simple, called the Sms Service, for sending simple messages to cellular phones...

public class SmsService : ISmsService

{

    private SmsConfig _config;

 

    public void SetConfig(SmsConfig config)

    {

        _config = config;

    }

 

    public void SendMessage(string number, string message)

    {

        Console.WriteLine("SMS message: {0} sent to: {1} with account: {2}", message, number, _config.UserName);

    }

 

    public class SmsConfig

    {

        private string _userName;

        private string _password;

        private int _retryAttempts;

 

        internal string UserName

        {

            get { return _userName; }

        }

 

        internal string Password

        {

            get { return _password; }

        }

 

        public int RetryAttempts

        {

            get { return _retryAttempts; }

            set { _retryAttempts = value; }

        }

 

        public void SetCredentials(string userName, string password)

        {

            _userName = userName;

            _password = password;

        }

    }

}


It also has a corresponding interface:

public interface ISmsService

{

    void SendMessage(string number, string message);

}


Now the point is that this class can not be constructred and configured by the container "as-is" because it requires something like this to happen:

SmsService service = new SmsService();

 

SmsService.SmsConfig config = new SmsService.SmsConfig();

config.SetCredentials("joe", "secret");

config.RetryAttempts = 3;

 

service.SetConfig(config);


Ack! Which doesn't fit in nicely with our concepts of constructor and setter injection for configuration...  so the answer is first off, to write a factory... can you guess what it'll look like?

public class SmsServiceFactory

{

    public ISmsService CreateService()

    {

        SmsService service = new SmsService();

 

        SmsService.SmsConfig config = new SmsService.SmsConfig();

        config.SetCredentials("joe", "secret");

        config.RetryAttempts = 3;

 

        service.SetConfig(config);

 

        return service;

    }

}


Well almost - but we don't like hard coding credentials, so we might refactor it a little... to say something like this:

public class SmsServiceFactory

{

    private string _userName;

    private string _password;

    private int _retryAttempts = 3;

 

    public SmsServiceFactory(string userName, string password)

    {

        _userName = userName;

        _password = password;

    }

 

    public int RetryAttempts

    {

        get { return _retryAttempts; }

        set { _retryAttempts = value; }

    }

 

    public ISmsService CreateService()

    {

        SmsService service = new SmsService();

 

        SmsService.SmsConfig config = new SmsService.SmsConfig();

        config.SetCredentials(_userName, _password);

        config.RetryAttempts = _retryAttempts;

 

        service.SetConfig(config);

 

        return service;

    }

}


Now how do we use this factory in lieu of registering the SmsService itself in the container... well first off, we need to add in the factory support facility... which is placed within a new part of the castle configuration we haven't seen until now (so far we've only be using the <components /> section) - here's how that looks:

<configuration>

 

  <configSections>

    <section name="castle"

        type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />

  </configSections>

 

  <castle>

    <facilities>

 

      <facility

        id="factorysupport"

        type="Castle.Facilities.FactorySupport.FactorySupportFacility, Castle.MicroKernel" />

 

    </facilities>


Nothing much to it, but now the factory support facility has augmented our container, so we can do this:

<configuration>

 

  <configSections>

    <section name="castle"

        type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />

  </configSections>

 

  <castle>

    <facilities>

 

      <facility

        id="factorysupport"

        type="Castle.Facilities.FactorySupport.FactorySupportFacility, Castle.MicroKernel" />

 

    </facilities>

    <components>

 

      <component id="smsService.Factory"

                type="IoC.Tutorials.Part11.SmsServiceFactory, IoC.Tutorials.Part11">

        <parameters>

          <userName>joe</userName>

          <password>secret</password>         

        </parameters>

      </component>

 

      <component id="smsService.default"

                type="IoC.Tutorials.Part11.ISmsService, IoC.Tutorials.Part11"

                factoryId="smsService.Factory"

                factoryCreate="CreateService" />

 

    </components>

  </castle>

 

</configuration>


Notice that we've registered our factory as a component, and called it smsService.Factory - and then we've registered the smsService.default (interface only) and added a couple of additional attributes, factoryId (which references our factory component) and factoryCreate - the name of the method in the factory which is responsible for creating instances of our sms service.

Because we now have the factory support facility installed, whenever a component is registered that facility will inspect it and see if the factoryId attribute appears in the components configuration and consequently defer it's activation to the referenced factory - pretty cool huh?

So, the million dollar question, does it work?  Well lets see... here's our code:

static void Main(string[] args)

{

    WindsorContainer container = new WindsorContainer(new XmlInterpreter());

 

    ISmsService smsService = container.Resolve<ISmsService>();

    smsService.SendMessage("+465556555", "testing testing...1.2.3");

 

    Console.Read();

}


And the output:

SMS message: testing testing...1.2.3 sent to: +465556555 with account: joe

At this point I was going to take a quick look into another factory-related facility, the TypedFactory, but it's currently broken in the main trunk ;o) so we'll wait until that's been resolved before finishing off this post.

Next time we'll have a quite look at revisiting a class design pattern, the decorator, when using the Windsor container.
posted @ Wednesday, April 25, 2007 7:11:04 AM (New Zealand Standard Time, UTC+12:00)    Comments [0] | Trackback |
10_setter_injection.png
So, lets have a look at setter injection - for this part I'll be refactoring the example in part 9 ... so if you recall in the last part we had a class that could send messages (to the console at least) - and it had a hard-coded string being used for the format... we'll I think it would be nice to "plug in" different message formatting, if we needed to... so first thing to do is create a necessary abstraction for formatting a message - which I've called the IMessageFormatter interface:

public interface IMessageFormatter

{

    string FormatMessage(string from, string to, string body);

}


Now, our old method for formatting a message (as part of the SecretMessageSender class) used to look like this:

public void SendMessage(string to, string body)

{

    Console.WriteLine("to: {0}\r\nfrom: {1}\r\n\r\n{2}", to, _from, _encoder.Encode(body));

}


So we just refactor that out into it's own class, like so:

public class DefaultFormatter : IMessageFormatter

{

    public string FormatMessage(string from, string to, string body)

    {

        return string.Format("to: {0}\r\nfrom: {1}\r\n\r\n{2}", to, from, body);

    }

}


And update the secret message sender to use this default formatter... by default!

public class SecretMessageSender

{

    private readonly IEncoder _encoder;

    private readonly string _from;

    private IMessageFormatter _formatter = new DefaultFormatter();

 

    public SecretMessageSender(string from, IEncoder encoder)

    {

        _from = from;

        _encoder = encoder;

    }

 

    public IMessageFormatter Formatter

    {

        get { return _formatter; }

        set { _formatter = value; }

    }

 

    public void SendMessage(string to, string body)

    {

        string encodedBody = _encoder.Encode(body);

        Console.WriteLine(_formatter.FormatMessage(_from, to, encodedBody));

    }

}


Now notice we automatically create a default formatter for the class to use... so at this point we can run the program just as in part 9 (Without having to make any changes to the container configuration) and everything works as it did... however, we can also optionally override the message formatter by registering an implementation in the container... the MicroKernel is smart enough to discover any publicly accessible properties with setters, look for any implementations, and inject them if they're registered in the container.

So first off lets create a different implementation - I'm going to use NVelocity (a template engine, which is used for one of the View engines that comes with monorail) this time along with a template - so first we have the alternative message formatter class:

public class NVelocityMessageFormatter : IMessageFormatter

{

    private readonly VelocityEngine _velocity;

    private readonly Template _template;

 

    public NVelocityMessageFormatter(string templateFile)

    {

        _velocity = new VelocityEngine();

        ExtendedProperties props = new ExtendedProperties();

        _velocity.Init(props);

        _template = _velocity.GetTemplate(templateFile);

    }

 

    public string FormatMessage(string from, string to, string body)

    {

        VelocityContext context = new VelocityContext();

        context.Put("from", from);

        context.Put("to", to);

        context.Put("body", body);

        context.Put("today", DateTime.Now);

 

        StringWriter writer = new StringWriter();

        _template.Merge(context, writer);

 

        return writer.ToString();

    }

}


And of course, we also need a template file (Which we will call message.vm) - this is what our template will look like:

 To:   $to

 From: $from

 Sent: $today

 

 ----------------------

 

 $body

 

 ----------------------


And finally the last part of the puzzle is to update the container configuration to include our alternative message formatter:

<configuration>

 

  <configSections>

    <section name="castle"

        type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />

  </configSections>

 

  <castle>

    <components>

      <component id="encoder.silly"

                service="IoC.Tutorials.Part10.IEncoder, IoC.Tutorials.Part10"

                type="IoC.Tutorials.Part10.SillyEncoder, IoC.Tutorials.Part10" />

 

      <component id="encoder.null"

                service="IoC.Tutorials.Part10.IEncoder, IoC.Tutorials.Part10"

                type="IoC.Tutorials.Part10.NullEncoder, IoC.Tutorials.Part10" />

 

      <component id="messageSender"

                type="IoC.Tutorials.Part10.SecretMessageSender, IoC.Tutorials.Part10">

        <parameters>SecretMessageSender

          <from>alex@bittercoder.com</from>

          <encoder>${encoder.null}</encoder>         

        </parameters>

      </component>

 

      <component id="fancyMessageFormatter"

                service="IoC.Tutorials.Part10.IMessageFormatter, IoC.Tutorials.Part10"

                type="IoC.Tutorials.Part10.NVelocityMessageFormatter, IoC.Tutorials.Part10">

        <parameters>

          <templateFile>message.vm</templateFile>

        </parameters>

      </component>

 

    </components>

  </castle>

 

</configuration>


And now if we give it a run, we get this output:

To:   hammet
From: alex@bittercoder.com
Sent: 25/04/2007 6:56:37 p.m.

----------------------

castle is great!

----------------------


So you can now see how a mix of constructor and setter injection can be used to achieve compulsory and optional dependencies - of course, we can also wire up properties to specific implementations - for instance in this last example we could change the config to read:

<component id="messageSender"

          type="IoC.Tutorials.Part10.SecretMessageSender, IoC.Tutorials.Part10">

  <parameters>SecretMessageSender

    <from>alex@bittercoder.com</from>

    <encoder>${encoder.null}</encoder>

    <Formatter>${fancyMessageFormatter}</Formatter>

  </parameters>

</component>

 

<component id="defualtMessageFormatter"

    service="IoC.Tutorials.Part10.IMessageFormatter, IoC.Tutorials.Part10"

    type="IoC.Tutorials.Part10.DefaultFormatter, IoC.Tutorials.Part10" />       

 

<component id="fancyMessageFormatter"

          service="IoC.Tutorials.Part10.IMessageFormatter, IoC.Tutorials.Part10"

          type="IoC.Tutorials.Part10.NVelocityMessageFormatter, IoC.Tutorials.Part10">

  <parameters>

    <templateFile>message.vm</templateFile>

  </parameters>

</component>


Where even though the default message formatter is registered first in the container, the fancy message formatter will be used because it's manually wired up in the messageSender's component parameters.

Next time will be having a look at factories and how to fit in with the Windsor container.
posted @ Wednesday, April 25, 2007 6:43:10 AM (New Zealand Standard Time, UTC+12:00)    Comments [0] | Trackback |
 Friday, April 20, 2007
I noticed that Ivan posted a list of tutorials etc. on his blog... I think it's worth also adding that there is a site dedicated to these kinds of resources, as well as snippets, recipes etc.  - it's still in it's infancy, but I think it will end up being quite invaluable as a resource for castle project users (here's hoping at any rate!)

http://using.castleproject.org/

posted @ Thursday, April 19, 2007 9:19:00 PM (New Zealand Standard Time, UTC+12:00)    Comments [2] | Trackback |
 Thursday, April 19, 2007
So the first 9 posts are up for the container tutorial series...  but I haven't made much mention of what can be done to play around with the windsor container yourself while reading this series.

Well first-off ... my thoughts on getting started with the Castle project are generally practical, first off I think it's worth grabbing the second release candidate - check out what castle can do, have a general play, maybe try to generate a monorail project and get it working... and then ... well I would probably uninstall it, and grab the latest cut of the castle project and contributed projects from svn and build it yourself using nant.

This gives you opportunities to use all the "new stuff" that gets added day by day (as well as bug fixes) but also ensures your actually capable of building and contributing patches for any issues you discover.

But that's a bit of an ask for someone experimenting, especially if you don't know anything about svn... so essentially for playing around with the basics of the windsor container, you only need 4 Castle assemblies from the bleeding edge, and I'll give them to you:

simple_container_references.png

And to make life easy I included those assemblies in a folder called "SharedLibs" as part of the following zip file, which contains the code for the first 9 parts... I'll do another code drop at the end of the container tutorials to round out the series for the sake of posterity... but everything you need for the first 9 tutorials is here:

Files for Parts 1 thru 9

Good Times ;o)


posted @ Thursday, April 19, 2007 10:31:40 AM (New Zealand Standard Time, UTC+12:00)    Comments [4] | Trackback |
9_constructor_injection.png

Constructor injection - so far inadvertantly we've been using setter inject to get our configuration information into our components... but you can also use constructors... the useful aspect of constructors is that it makes those parameters compulsory...

So lets take a look at the code for this post, first off there's an interface for a service which takes care of encoding a string for us:

public interface IEncoder

{

    string Encode(string source);

}


And then we have no classes which implement this service, one we can use when testing to not encode things at all - and another which encodes a string using our particularly strong "silly" encryption...

NullEncoder.cs

public class NullEncoder : IEncoder

{

    public string Encode(string source)

    {

        return source;

    }

}


SillyEncoder.cs

public class SillyEncoder : IEncoder

{

    private char[] _mixedUp = "YACBDFEGIHJLKMONPRSQTUWVXZ".ToCharArray();

 

    public string Encode(string source)

    {

        string upperSource = source.ToUpper();

        char[] encoded = new char[source.Length];

        for (int i = 0; i < encoded.Length; i++)

        {

            encoded[i] = MapCharacter(upperSource[i]);

        }

        return new string(encoded);

    }

 

    private char MapCharacter(char ch)

    {

        if ((ch >= 'A') && (ch <= 'Z'))

        {

            return _mixedUp[ch - 'A'];

        }

        return ch;

    }

}


When then have a class for sending messages (currently we just send it to the console, so we can print them out and paste them onto the back of postcards) ... so lets have a look at that class:

public class SecretMessageSender

{

    private readonly IEncoder _encoder;

    private readonly string _from;

 

    public SecretMessageSender(string from, IEncoder encoder)

    {

        _from = from;

        _encoder = encoder;

    }

 

    public void SendMessage(string to, string body)

    {

        Console.WriteLine("to: {0}\r\nfrom: {1}\r\n\r\n{2}", to, _from, _encoder.Encode(body));

    }

}


Notice the lack of default constructor, and that the constructor also takes an instance of type IEncoder... now Castle is smart enough to do two things for us:
  • Throw an exception if we try to get an isntance of SecreteMessageSender without having set the from configuration parameter.
  • Find the default implementation of the IEncoder registered in the container, and to supply that as the value for the second argument in the constructor.
So lets have a look at the application itself:

static void Main(string[] args)

{

    WindsorContainer container = new WindsorContainer(new XmlInterpreter());

 

    SecretMessageSender sender = container.Resolve<SecretMessageSender>();

 

    sender.SendMessage("hammet", "castle is great!");

 

    Console.Read();

}


And finally our configuration:

<configuration>

 

  <configSections>

    <section name="castle"

        type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />

  </configSections>

 

  <castle>

    <components>

      <component id="encoder.silly"

                service="IoC.Tutorials.Part9.IEncoder, IoC.Tutorials.Part9"

                type="IoC.Tutorials.Part9.SillyEncoder, IoC.Tutorials.Part9" />

 

      <component id="encoder.null"

                service="IoC.Tutorials.Part9.IEncoder, IoC.Tutorials.Part9"

                type="IoC.Tutorials.Part9.NullEncoder, IoC.Tutorials.Part9" />

 

      <component id="messageSender"

                type="IoC.Tutorials.Part9.SecretMessageSender, IoC.Tutorials.Part9">

        <parameters>SecretMessageSender

          <from>alex@bittercoder.com</from>

        </parameters>

      </component>

 

    </components>

  </castle>

 

</configuration>


So you can see we've registered both implementations of the encoder, with the silly encoder being first - so as mentioned in the last part - this will be the default - and we also register our message sender (with the required from parameter)... so what happens when we run it:

to: hammet
from: alex@bittercoder.com

CYSQLD IS ERDYQ!


Cool... but what if wanted to send an unencrypted message... well we have a few options:
  • Swap the order in which the implementations are registered
  • Remove / comment out any implementations we don't want (so comment out the silly encoder)
  • Or reference the implementation we specifically want to wire up to, using it's identifier...
So lets have a look at the last one...

<configuration>

 

  <configSections>

    <section name="castle"

        type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />

  </configSections>

 

  <castle>

    <components>

      <component id="encoder.silly"

                service="IoC.Tutorials.Part9.IEncoder, IoC.Tutorials.Part9"

                type="IoC.Tutorials.Part9.SillyEncoder, IoC.Tutorials.Part9" />

 

      <component id="encoder.null"

                service="IoC.Tutorials.Part9.IEncoder, IoC.Tutorials.Part9"

                type="IoC.Tutorials.Part9.NullEncoder, IoC.Tutorials.Part9" />

 

      <component id="messageSender"

                type="IoC.Tutorials.Part9.SecretMessageSender, IoC.Tutorials.Part9">

        <parameters>SecretMessageSender

          <from>alex@bittercoder.com</from>

          <encoder>${encoder.null}</encoder>

        </parameters>

      </component>

 

    </components>

  </castle>

 

</configuration>


Notice the way the parameter is formatted - instead of the markup for a property reference which starts with a hash (#) we start service references with a dollar sign ($) and then surround the components identifier with braces.

And the results of running the app now:

to: hammet
from: alex@bittercoder.com

castle is great!


And thats our first look at constructor injection... we will revisit constructor injection again in later posts, however the next part will look at setter injection.
posted @ Thursday, April 19, 2007 9:37:05 AM (New Zealand Standard Time, UTC+12:00)    Comments [0] | Trackback |

8_implementations_by_key.png
So far we've looked at registering one implementation for any one service, you could call them the "default" implementation, because it's the implementation the container returns when you ask for that service... but your not limited to only having one implementation registered, and the way to do this is by giving each implementation that's registered a unique key.

There are many reasons for doing this... but in this example we'll look at one reason - because we want to vary the configuration info used...

So here's our component's code:

public class FileReader

{

    private string _fileName;

 

    public string FileName

    {

        get { return _fileName; }

        set { _fileName = value; }

    }

 

    public string ReadToEnd()

    {

        return File.ReadAllText(_fileName);

    }

}


It's a simple class which lets us read the contents of a file as many times as we like... so we're going to try registering it twice, with different configurations (and different identifiers) in the container.

<configuration>

 

  <configSections>

    <section name="castle"

        type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />

  </configSections>

 

  <castle>

    <components>

      <component id="reader.file1" type="IoC.Tutorials.Part8.FileReader, IoC.Tutorials.Part8">

        <parameters>

          <FileName>file1.txt</FileName>

        </parameters>

      </component>

      <component id="reader.file2" type="IoC.Tutorials.Part8.FileReader, IoC.Tutorials.Part8">

        <parameters>

          <FileName>file2.txt</FileName>

        </parameters>

      </component>

    </components>     

  </castle>

 

</configuration>


So far so good, lets have a look at the app itself - notice that the file1Reader and file2Reader instances are "resolved" with an extra parameter - which is the id, or key, used to select a particular implementation... but what about the default instance, which reader will that be?

static void Main(string[] args)

{

    WindsorContainer container = new WindsorContainer(new XmlInterpreter());

 

    FileReader defaultReader = container.Resolve<FileReader>();

    FileReader file1Reader = container.Resolve<FileReader>("reader.file1");

    FileReader file2Reader = container.Resolve<FileReader>("reader.file2");

 

    Console.WriteLine("Default contents: {0}", defaultReader.ReadToEnd());

    Console.WriteLine("File1 contents: {0}", file1Reader.ReadToEnd());

    Console.WriteLine("File2 contents: {0}", file2Reader.ReadToEnd());

 

    Console.Read();

}


And here's the results of running the program:

Default contents: This is the contents of file 1.
File1 contents: This is the contents of file 1.
File2 contents: This is the contents of file 2.


So you can see that file1Reader and defaultReader are the same - and file1Reader was the first FileReader to be registered in the container -and that's the rule, the first one registered is the default... which is another example of convention over configuration with Castle, rather then having to explicity denote which is the default with some more xml noise.

Though with a little thinking you can find plenty of ways to access implementations by key to solve certain problems, for the unimaginative ... how about an application where you can provide a uri to send plain text messages to ... and depending on the scheme you need to find an implementation that can do the work of sending the message.

You have a bunch of destinations, addressed by Uri...
  • file://c:/temp/log.txt
  • ftp://ftp.google.com/log.txt
  • http://www.bittercoder.com/SimpleMessageService.aspx
  • fax://64215559555
And then you can just register the implementations/schemes you wish to support...
  • messageSender.file
  • messageSender.ftp
  • messageSender.http
  • messageSender.fax
And then look them up as required, using something like

Uri uri;

// blah blah blah

string key = "messageSender."+ uri.Scheme;

if (container.Kernel.HasComponent(key))

{

    ISender sender = container.Resolve<ISender>(key);

    // send the message at this point...

    sender.SendMessage("hi there");

}

else

{

    // no implementation registered...

    throw new NotImplementedException("no sender registered for scheme: " + uri.Scheme);

}


Personally I wouldn't do this ;o) but there's certainly no technical reason why you couldn't... notice how we check that a component is registered for that key - we could just let the container throw an exception... but then we wouldn't know how to check that a container has a certain key registered in it, would we!

The next part will be on constructor injection... and following that setter injection.
posted @ Thursday, April 19, 2007 7:23:14 AM (New Zealand Standard Time, UTC+12:00)    Comments [0] | Trackback |
 Wednesday, April 18, 2007

So I've decided to do a quick little series on the windsor container - all up It'll probably cover 15 to 20 posts... each one should be very short, covering some small concept... just a little nugget.

This series will be a little different then most discussions on IoC and containers... rather then get in your face with concepts like dependency injection, and encouraging testability, it's just going to focus on the container itself, and the ways you can use it... I'm not sure if it'll be of any value, but If nothing else to gets me back into blogging regularly.

The first 7 parts are already up:

I'll be posting them in batches... so the series should be finished within a week.

posted @ Tuesday, April 17, 2007 7:51:40 PM (New Zealand Standard Time, UTC+12:00)    Comments [4] | Trackback |
 Tuesday, April 17, 2007
7_switching_implementations.png
So this time we're going to look at how you can not only change configuration for a component at runtime, but actually change which component is doing the work for us... powerful stuff :)

So the "trick" here is to abstract out what features we expect our component to support from it's implementation, and stuff those into an interface... so in this case I have an idea for a simple service which lets us get the "message of the day" - so lets look at my interface:

public interface IMessageOfTheDay

{

    string GetMessageOfTheDay();

}


Now we have two implementations of this service, one that lets us have a static message set via configuration:

public class StaticMessageOfTheDay : IMessageOfTheDay

{

    private string _message;

 

    public string Message

    {

        set { _message = value; }

    }

 

    public string GetMessageOfTheDay()

    {

        return _message;

    }

}


And another, which goes to wiki quotes and grabs the quote of the day:

public class WikiQuotesMessageOfTheDay : IMessageOfTheDay

{

    public string GetMessageOfTheDay()

    {

        WebClient client = new WebClient();

        string content = client.DownloadString("http://en.wikiquote.org/wiki/Main_Page");

 

        string toFind = "<div style=\"background: #fff5f5\">";

        int start = content.IndexOf(toFind) + toFind.Length;

        int length = content.IndexOf("<a", start) - start;

 

        return content.Substring(start, length);

    }

}


so - our program is pretty simple:

private static void Main(string[] args)

{

    WindsorContainer container = new WindsorContainer(new XmlInterpreter());

 

    IMessageOfTheDay motd = container.Resolve<IMessageOfTheDay>();

 

    Console.WriteLine("MOTD: {0}", motd.GetMessageOfTheDay());

 

    Console.Read();

}


And so how do we swap between implementations, well we need to introduce an additional attribute to our components configuration called "service" where we specifiy what service our component implements... in this case it will be "IMessageOfTheDay" - so heres the configuration when using the Static MOTD class:

<configuration>

 

  <configSections>

    <section name="castle"

        type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />

  </configSections>

 

  <castle>

    <components>

      <component id="motd.service"

                service="IoC.Tutorials.Part7.IMessageOfTheDay, IoC.Tutorials.Part7"

                type="IoC.Tutorials.Part7.StaticMessageOfTheDay, IoC.Tutorials.Part7">

        <parameters>

          <message>Welcome to my container tutorials</message>

        </parameters>

      </component>

    </components>

  </castle>

 

</configuration>


And running the program gives us:

MOTD: Welcome to my container tutorials

But we can now swap to a different component with the same service at run-time, and get a different implementation... here's how that looks:

<configuration>

 

  <configSections>

    <section name="castle"

        type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />

  </configSections>

 

  <castle>

 

    <components>

      <component id="motd.service"

          service="IoC.Tutorials.Part7.IMessageOfTheDay, IoC.Tutorials.Part7"

          type="IoC.Tutorials.Part7.WikiQuotesMessageOfTheDay, IoC.Tutorials.Part7" />

    </components>

  </castle>

 

</configuration>


And now running the program gives us:

MOTD: Man is not an end but a beginning. We are at the beginning of the second week. We are children of the eighth day.

A quote from Thorton Wilder - exciting huh, this seperation between interface and implementation is what makes doing so many cool things with an Inversion of control container possible... soak up the idea.

Next time we'll take a look at getting different implementations by using their "key" - have you been wondering what the "id" attribute is for in our component definitions?? Well wonder no more.
posted @ Tuesday, April 17, 2007 11:34:46 AM (New Zealand Standard Time, UTC+12:00)    Comments [0] | Trackback |
6_switching_lifestyles.png
Switching lifestyles is the theme of this post... so what's that all about?

Well first off - what is a lifestyle, in basic terms it specifies how many instances will be available at any one time from the container - so at the one extreme we have components with a transient lifestyle, where any requests from those components will return new instances - and at the opposite end of the spectrum we have singleton components, where there is only one instance for the whole container - there are other types of lifestyle, but we'll