In a afternoon of July I was working refactorizing a web-project. We was using the XML configuration of Castle.Windsor and Santiago Leguiza asked me a way to simplify the configuration.
The natural way to go is the configuration via Fluent-Interface. At that time my opinion about fluent-conf of the IoC was not so good because the fluent-conf needs references to each layer of the application. We have an IApplicationInitializer, for the application startup, but this time I need “something” with a more specific responsibility: wire any application part.
To give a name to a class is not so easy for me… probably because, in my opinion, the name should define the class responsibility. English, other than C#, is not my best, you know… using a mix between internet and my very old Italian-English dictionary, after 15 minutes or so, I have found a pretty good and funny name: GuyWire.
A guy-wire or guy-rope is a tensioned cable designed to add stability to structures. One end of the cable is attached to the structure, and the other is anchored to the ground at a distance from the structure's base.
Happy to have found the name I had twitted it ( first, second )
The interface
The interface is very very simple:
public interface IGuyWire
{
/// <summary>
/// Application wire.
/// </summary>
/// <remarks>
/// IoC container configuration (more probably conf. by code).
/// </remarks>
void Wire();
/// <summary>
/// Application dewire
/// </summary>
/// <remarks>
/// IoC container dispose.
/// </remarks>
void Dewire();
}
Using this interface is easy to realize that you may have different implementation for different IoC or, better, for different scenarios (for example in different test projects or the web-app and the wcf-service).
You can declare the IGuyWire in a separated very simple assembly where the only reference needed is System (nothing more).
Well… the interface is done but if I need to instantiate a concrete implementation I will have again the same problem… my application should know about the IoC and everything about all other parts… hmmm… I’m needing something to inject the injector.
again the solution is so simple as few code lines:
/// <summary>
/// Helper class to get the <see cref="IGuyWire"/> concrete implementation
/// from application config.
/// </summary>
/// <remarks>
/// The appSetting section should have a key named "GuyWire" (case insensitive)
/// <example>
/// <![CDATA[
/// <appSettings>
/// <add key='GuyWire' value='YourCompany.Product.Wiring.IoC_Fx.GuyWire, YourCompany.Product.Wiring.IoC_Fx'/>
/// </appSettings>"
/// ]]>
/// </example>
/// </remarks>
public static class ApplicationConfiguration
{
private const string GuyWireConfKey = "guywire";
private const string GuyWireConfMessage =
@"The GuyWire was not configured.
Example
<appSettings>
<add key='GuyWire' value='YourCompany.Product.Wiring.IoC_Fx.GuyWire, YourCompany.Product.Wiring.IoC_Fx'/>
</appSettings>";
/// <summary>
/// Read the configuration to instantiate the <see cref="IGuyWire"/>.
/// </summary>
/// <returns>The instance of <see cref="IGuyWire"/>.</returns>
/// <exception cref="ApplicationException">
/// If the key='GuyWire' was not found or if the <see cref="IGuyWire"/> can't be instancied.
/// </exception>
public static IGuyWire GetGuyWire()
{
var guyWireClassKey =
ConfigurationManager.AppSettings.Keys.Cast<string>().FirstOrDefault(k => GuyWireConfKey.Equals(k.ToLowerInvariant()));
if (string.IsNullOrEmpty(guyWireClassKey))
{
throw new ApplicationException(GuyWireConfMessage);
}
var guyWireClass = ConfigurationManager.AppSettings[guyWireClassKey];
var type = Type.GetType(guyWireClass);
try
{
return (IGuyWire)Activator.CreateInstance(type);
}
catch (MissingMethodException ex)
{
throw new ApplicationException("Public constructor was not found for " + type, ex);
}
catch (InvalidCastException ex)
{
throw new ApplicationException(type + "Type does not implement " + typeof(IGuyWire), ex);
}
catch (Exception ex)
{
throw new ApplicationException("Unable to instantiate: " + type, ex);
}
}
}
The ApplicationConfiguration class can stay in the same assembly of the IGuyWire, so, in my application, I will have a reference only to the assembly containing the IGuyWire and the ApplicationConfiguration and, the Global.asax for example, will look like:
private static IGuyWire guywire;
void Application_Start(object sender, EventArgs e)
{
guywire = ApplicationConfiguration.GetGuyWire();
guywire.Wire();
}
void Application_End(object sender, EventArgs e)
{
guywire.Dewire();
}
Doing so my application does not need a strongly reference neither the IoC nor any other concrete implementations of my interfaces.
How use the GuyWire
Some other pieces of the definition:
When steel cable is used, the guys are divided by insulators into multiple sections…
Does it match with something ? Yes it does. We have a web-application a wcf-service and various tests projects… our application is composed by different layers, each layer has its way to be wired and we can re-use the same wiring in different areas. As result of this concept I have an abstract implementation for Castle.Windsor:
namespace YourCompany.YourPrjCodeName.Wiring.Castle
{
public abstract class AbstractGuyWire : IGuyWire
{
protected WindsorContainer Container;
public void Wire()
{
if (Container != null)
{
Dewire();
}
Container = new WindsorContainer();
foreach (var guyWire in GuyWires)
{
guyWire.Wire();
}
}
public void Dewire()
{
if (Container != null)
{
Container.Dispose();
}
Container = null;
}
protected abstract IEnumerable<IGuyWire> GuyWires { get; }
}
}
The concrete class for the web-application is:
public class GuyWire : AbstractGuyWire
{
#region Overrides of AbstractGuyWire
protected override IEnumerable<IGuyWire> GuyWires
{
get
{
yield return new ServiceLocatorGuyWire(Container);
yield return new NhWebSessionManagementGuyWire(Container);
yield return new PersistenceGuyWire(Container);
yield return new ModelsGuyWire(Container);
}
}
#endregion
}
and for the wcf-service (to know where I’m using the ApplicationConfiguration class have a look to this post) :
public class ExternalServicesGuyWire : AbstractGuyWire
{
#region Overrides of AbstractGuyWire
protected override IEnumerable<IGuyWire> GuyWires
{
get
{
yield return new ServiceLocatorGuyWire(Container);
yield return new NhWebSessionManagementGuyWire(Container);
yield return new PersistenceGuyWire(Container);
yield return new WcfServicesGuyWire(Container);
}
}
#endregion
}
in each test-suite you can re-use same GuyWires depending on which layer you are testing and/or the type of the test (integration test or not).
P.S. another pending task was done!! thanks to be patient.