Fluent and WindsorContainer Facilities

2 views
Skip to first unread message

kurtharriger

unread,
Feb 23, 2009, 11:09:07 AM2/23/09
to Castle Project Users
I would like to switch to using fluent registration for most things
rather than configuration files, but also have the ability specify add/
replace components via configuration.

The monorail project has a sample container implementation here.http://
www.castleproject.org/monorail/documentation/trunk/integration/windsor.html

However there seems to be a slight problem with this approach. The
StartableFacility and MonorailFacility and possibly others do not work
properly if registered after the component is registed in the config
file rather than in the code. The reason it seems is that the windsor
container installs all components in the configuration in base
constructor before the WebAppContainer constructor has a chance to run
and the ComponentRegisterd/ComponentModelCreated events are not
recieved by the facility if the facility is added after the component
has been installed. It seems then that it would be necessary to
install the facilities before the configuration file is parsed. The
default installer seems to register facilities in configuration before
installing any components in the configuration but the question then
is how to install facilities in code before loading the components in
the configuaion file.

I was thinking perhaps I could create the container, add facilities
and then use install to load the configuration but this too seems to
have some problems since some facilities have additional configuration
it doesn't seem that I can preregister all facilities prior to
installation of the configuration file.

Ideally, I was wanting to create a custom facility to perform the
default component registration and using facility configuration to
specify assemblies to search type and/or other administrative settings
and configuration overrides. And ideally, if no configuration is
specified then the code would automatically add the default
registration facility and use the default conventions to search for
components, but it seems the facilities need added via the
configuration file rather then code otherwise ComponetRegistered
events don't get recieved.

How have others handled this?

Thanks

Ayende Rahien

unread,
Feb 23, 2009, 3:38:47 PM2/23/09
to castle-pro...@googlegroups.com
Create the actual component registration outside the ctor.

Kurt Harriger

unread,
Feb 24, 2009, 5:26:40 PM2/24/09
to castle-pro...@googlegroups.com
It''s not so much when to register the components that causes an issue
but when to register the facilities. As long as I don't try add or
override existing components in castle config file code based
registration is fine, but if I want the user to be able to replace
implementations using the config file things start to fall apart.
Here are some methods I've tried with no success.

castle.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<facilities>
<facility id="testfacility">
<assemblies>
<assembly>SomeAssembly</assembly>
</assemblies>
</facility>
</facilities>
<components>
<component id="somecomponent"
service="Container.Tests.ISomeComponent, Container.Tests"
type="Container.Tests.SomeSpecializedComponent, Container.Tests"/>
</components>
</configuration>

TestFacilityFixture.cs

namespace Container.Tests
{
interface ISomeComponent { }
public class SomeComponent : ISomeComponent { }
public class SomeSpecializedComponent : SomeComponent { }

public class TestFacility : AbstractFacility
{
public static IList<string> RegisteredComponents;
public static bool ConfigurationAvailable;

protected override void Init()
{
ConfigurationAvailable = FacilityConfig != null &&
FacilityConfig.Children["assemblies"] != null;
Kernel.ComponentRegistered += Kernel_ComponentRegistered;
}

void Kernel_ComponentRegistered(string key,
Castle.MicroKernel.IHandler handler)
{
RegisteredComponents.Add(key);
}
}

[TestFixture]
public class TestFacilityFixture
{
const string ConfigFile = "castle-facility.config";

[SetUp]
public void SetUp()
{
TestFacility.ConfigurationAvailable = false;
TestFacility.RegisteredComponents = new List<string>();
}
static void VerifyDesiredState(IWindsorContainer container)
{
Assert.That(TestFacility.ConfigurationAvailable, Is.True,
"configuration settings were not available");
Assert.That(TestFacility.RegisteredComponents,
Has.Member("somecomponent"), "Facility was not notified of the
component's registration");
Assert.That(container.Resolve<ISomeComponent>(),
Is.InstanceOfType(typeof(SomeSpecializedComponent)), "Expecting
instance of SomeSpecializedComponent");
}

[Test]
//FAILS:
// most examples I've seen register facilities and components
in constructor after config is parsed
// but faicilities added after config are not notified when
components are registered
public void TestConstructorInitialization()
{
var container = new TestContainer(new XmlInterpreter(ConfigFile));
VerifyDesiredState(container);
}
[Test]
//FAILS:
// if facilities added before parsing configuration the
facility configuration settings are not available
public void TestInstallStrategy()
{
var container = new TestContainer();
container.RegisterFacilities();
container.Install(Configuration.FromXmlFile(ConfigFile));
container.RegisterComponents();

VerifyDesiredState(container);
}
[Test]
//FAILS:
// Installer is run during creation even though no config file
has been specified yet
public void TestSpecializedInstallerStrategy()
{
var container = new TestContainer(new DefaultKernel(), new
SpecializedComponentInstaller());
container.Install(Configuration.FromXmlFile(ConfigFile));
container.RegisterComponents();
VerifyDesiredState(container);
}
[Test]
//FAILS:
// Seems that the config file settings are not merged
public void TestAddFacilityToConfigurationStor()
{
var configStore = new DefaultConfigurationStore();
var facilityTypeInfo = new MutableConfiguration("facility");
facilityTypeInfo.Attribute("id", "testfacility");
facilityTypeInfo.Attribute("type",
typeof(TestFacility).AssemblyQualifiedName);
configStore.AddFacilityConfiguration("testfacility",
facilityTypeInfo);

var container = new TestContainer(configStore);
container.Install(Configuration.FromXmlFile(ConfigFile));
container.RegisterComponents();
VerifyDesiredState(container);
}
}

public class TestContainer : WindsorContainer
{
public TestContainer() { }
public TestContainer(IKernel kernel, IComponentsInstaller
installer) : base(kernel, installer) { }
public TestContainer(IConfigurationStore configurationStore) :
base(configurationStore) { }

public TestContainer(IConfigurationInterpreter interpreter) :
base(interpreter)
{
RegisterFacilities();
RegisterComponents();
}


public void RegisterFacilities()
{
if(Kernel.GetFacilities().Count(t=>t is TestFacility) ==
0) AddFacility<TestFacility>("testfacility");
}
public void RegisterComponents()
{
if(!Kernel.HasComponent("somecomponent"))
{

Register(Component.For<ISomeComponent>().ImplementedBy<SomeComponent>().Named("somecomponent"));
}
//....
}


}

class SpecializedComponentInstaller : DefaultComponentInstaller
{
protected override void
SetUpFacilities(Castle.Core.Configuration.IConfiguration[]
configurations, IWindsorContainer container)
{
base.SetUpFacilities(configurations, container);
if (container.Kernel.GetFacilities().Count(t => t is
TestFacility) == 0)
container.AddFacility<TestFacility>("testfacility");

}
}
}


If you specify the facility type rather than just id in config file
then all tests pass. (<facility id="testfacility"
type="Container.Tests.TestFacility, Container.Tests">)
So if I want to override any services in the config file it seems I
need to register the facility in the config file rather than in code.
The best alternative I can think of is not to use the component type
attribute to override existing services but instead to add an element
in the facility configuration like this I guess:
<somecomponent>Container.Tests.SomeSpecializedComponent,
Container.Tests</somecomponent>


Thoughts?

- Kurt

Reply all
Reply to author
Forward
0 new messages