Note: I owe this knowledge to my friend Fabio Maulo, so I would like to thank him for teaching me and letting me share.
I will show you in this post a nice way to configure your IoC container and some other aspects of your applications. I assume for this article that you have good knowledge of dependency injection and inversion of control.
There was a time when we used to configure our containers with xml, and all was fine. Then we started to use fluent and strongly typed interfaces and a problem became more frequent and acute. Basically, when we use a fluent interface, we do something like this:
container.Register(Component.For<IAlbumRepository>() .ImplementedBy<AlbumRepository>() .LifeStyle.Transient);
IAlbumRepository is in Chinook.Data and AlbumRepository is in Chinook.Data.Impl.
The problem is not *how* but *where*. Where do you do this?
Most people will say “The startup project”. One of the goal that we look when using IoC is:
..decoupling of the execution of a certain task from implementation.
Pay attention to the “Decoupling” part. If you want “decoupling”: Why do you add all those references in your startup project?
This is a NDepend extracted graph of the Northwind example that comes with Sharp Architecture:
If you look at the Northwind.Web assembly you will see that it has a reference even to NHibernate. It has a reference to Northwind.Data (repositories that use NHibernate). The contract interface of repositories is in Northwind.Core. Also you can see that the start up project need a reference to Castle Container. If I want a loosely coupled solution, I don’t want some of those references.
Pay atention; the northwind example of Sharp Architecture is a very good example, is a good architecture, and this is neither a complain nor a criticism. I’m pretty sure that it defines interfaces and implementations separately, as I’m pretty sure that controllers depends upon IRepositories. But, I think it has a problem at reference level. The main problem is because the initialization of the container is in a wrong place. You could see the initialization script here.
So, what I’m looking for is:
The solution is very simple. Take the container initialization to another place.
We used to call this place “The GuyWire”, in italian “Cavo Portante”, in spanish “Cable Maestro”:
A guy-wire or guy-rope is a tensioned cable designed to add stability to structures
Two simple rules to remember:
The guywire is an excellent place to “configure” the whole application. I used to separate the configuration in different aspects. Here some examples:
You can explore the configurators of ChinookMediaManager here.
The guywire project has an special “Output Path”:
So, the path is the “Startup” project.
Secondly, in the App.Config, I have a config like this:
<appSettings> <add key="GuyWire" value="ChinookMediaManager.GuyWire.GeneralGuyWire, ChinookMediaManager.GuyWire" /> </appSettings>
And finally, in my App.Xaml.cs:
public partial class App : Application { private readonly IGuyWire guyWire = ApplicationConfiguration.GetGuyWire(); public App() { guyWire.Wire(); } protected override void OnStartup(StartupEventArgs e) { var viewFactory = ServiceLocator.Current.GetInstance<IViewFactory>(); viewFactory.ShowView<BrowseArtistViewModel>(); } protected override void OnExit(ExitEventArgs e) { guyWire.Dewire(); base.OnExit(e); } }
The interface IGuyWire and other classes needed for doing this, are in unhaddins.
The reference graph of the Chinook Media Manager, is as follows:
I really like the idea of have different parts of the application configuration in separated classes. I used to reuse this configurators alone in my Tests. But, as a I said before, the guywire should not be referenced by any project. So, my trick is add the configurator as a “linked file” to the test project:
And this is a sample:
[TestFixture] public class AlbumValidationFixture { private IWindsorContainer container; [TestFixtureSetUp] public void FixtureSetUp() { container = new WindsorContainer(); var configurator = new NHVConfigurator(); configurator.Configure(container); } [Test] public void title_constraints() { GetConstraint<Album, NotEmptyAttribute>(a => a.Title) .Message .Should().Be.EqualTo("Title should not be null."); GetConstraint<Album, LengthAttribute>(a => a.Title) .Should().Be.OfType<LengthAttribute>() .And.ValueOf.Message.Should().Be.EqualTo("Title should not exceed 200 chars."); } }
I will not talk about this long break in the series. If you are interested in Wpf and Nhibernate, you should be happy because; I'M BACK!