Table of contents
- 1 Externalizing the container configuration
- 2 Passing configuration parameters
- 3 Choosing the notifier
- 4 Passing a list of notifiers to HttpServiceWatcher
Using the configuration file
What we have developed so far might have sparked a few questions:
- Which IFailureNotifier implementation is the HttpServiceWatcher getting? How can I change that?
- What if I want to give HttpServiceWatcher a list of IFailureNotifier instead?
- How can I configure HttpServiceWatcher to receive say, an URL?
Those are good questions. By default the container will supply the first service registered. So the HttpServiceWatcher is getting an EmailFailureNotifier instance. We can change that using a service override.
You can pass a list of notifiers too, but you need to inform the container about what instances do you want to pass. Suppose you had ten implementations of IFailureNotifier. You must tell which ones should be passed. More on that shortly.
Finally, yes, you can supply an URL to HttpServiceWatcher and why not a list of emails to EmailFailureNotifier? We will see how to make all those things.
Externalizing the container configuration
So far we have used code to configure our container. Let's make this configuration external to the code, so we have more flexibility and we can make changes without recompiling the application.
The configuration is very minimum and, by default, is done in Xml. We can have a standalone xml file or use the configuration associated with the AppDomain. Let's use the latter.
Add an App.config file to your Visual Studio project:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
</configuration>
Visual Studio will take care of copying and renaming the file to your executable assembly name, for example: GettingStartedPart1.exe.config
Now add a section to configure Windsor Container:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section
name="castle"
type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />
</configSections>
<castle>
<components>
</components>
</castle>
</configuration>
We can now move the configuration from the App.cs to the configuration, on the "castle" section. It will look like the following:
<castle>
<components>
<component
id="httpservicewatcher"
type="GettingStartedPart1.HttpServiceWatcher, GettingStartedPart1" />
<component
id="email.notifier"
service="GettingStartedPart1.IFailureNotifier, GettingStartedPart1"
type="GettingStartedPart1.EmailFailureNotifier, GettingStartedPart1" />
<component
id="alarm.notifier"
service="GettingStartedPart1.IFailureNotifier, GettingStartedPart1"
type="GettingStartedPart1.AlarmFailureNotifier, GettingStartedPart1" />
<component
id="form.component"
type="GettingStartedPart1.Form1, GettingStartedPart1" />
</components>
</castle>
Finally, we need to create the Windsor Container passing a configuration interpreter and a resource:
using Castle.Core.Resource; using Castle.Windsor; using Castle.Windsor.Configuration.Interpreters; public class App { public static void Main() { IWindsorContainer container = new WindsorContainer( new XmlInterpreter(new ConfigResource("castle"))); // Request the component to use it Form1 form = (Form1) container[typeof(Form1)]; // Use the component Application.Run(form); // Release it container.Release(form); } }
Test your work and everything should work as expected.
Passing configuration parameters
Let's improve our HttpServiceWatcher to receive an URL that it should use to make request on. First, is this URL a necessary or optional parameter? Let's assume it is optional, so we should use a property:
public class HttpServiceWatcher { private IFailureNotifier notifier; private string url = "default url"; public HttpServiceWatcher(IFailureNotifier notifier) { this.notifier = notifier; } public string Url { get { return url; } set { url = value; } } ...
How to configure this parameter with the configuration? Simple: use the parameters node:
<component
id="httpservicewatcher"
type="GettingStartedPart1.HttpServiceWatcher, GettingStartedPart1">
<parameters>
<Url>different url</Url>
</parameters>
</component>
Each node enclosed by the parameters node should be named after a property or a constructor argument.
Choosing the notifier
As stated before, there are two notifiers and the HttpServiceWatcher is requesting one. Which one will it get? By default, the first one registered, which is the EmailFailureNotifier. But suppose you want to change that, how would you do it?
We need to make a service override. What does that mean? A service override a configuration entry that changes the MicroKernel behavior when resolving dependency. In the end, we need to change the configuration passing a different service to the constructor argument.
public HttpServiceWatcher(IFailureNotifier notifier) { this.notifier = notifier; }
<component
id="httpservicewatcher"
type="GettingStartedPart1.HttpServiceWatcher, GettingStartedPart1">
<parameters>
<notifier>${alarm.notifier}</notifier>
<Url>different url</Url>
</parameters>
</component>
The ${} notation is called service lookup.
Passing a list of notifiers to HttpServiceWatcher
Suppose that you are not satisfied with the fact that the HttpServiceWatcher is receiving just one notifier instance. Well, let's make it receive an array of notifiers:
public class HttpServiceWatcher { private IFailureNotifier[] notifiers; private string url = "default url"; public HttpServiceWatcher(IFailureNotifier[] notifiers) { this.notifiers = notifiers; } ...
On our configuration, we need to make a few changes:
<component
id="httpservicewatcher"
type="GettingStartedPart1.HttpServiceWatcher, GettingStartedPart1">
<parameters>
<notifiers>
<array>
<item>${email.notifier}</item>
<item>${alarm.notifier}</item>
</array>
</notifiers>
<Url>different url</Url>
</parameters>
</component>
Proceed with Getting More.