Logo

NHibernate

The object-relational mapper for .NET

NHibernate and WPF: The GuyWire

Note: I owe this knowledge to my friend Fabio Maulo, so I would like to thank him for teaching me and letting me share.

Introduction

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.

The problem

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:

  • I don’t want references to concrete implementations in my startup project.
  • I don’t want to depend upon a specific IoC container technology.
  • And finally I don’t want to configure my container in the startup project, its is another “aspect” of my application like the repositories!

The Solution

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:

  1. In order to configure service and implementers fluently, the GuyWire will reference everything.
  2. Because the guy-wire reference everything, the GuyWire can not be referenced.

The guywire is an excellent place to “configure” the whole application. I used to separate the configuration in different aspects. Here some examples:

  • ORM configuration
  • Constraint Validator configuration
  • Repositories configuration
  • Entities configuration
  • ViewModel configuration
  • Controllers configuration
  • Views configuration

You can explore the configurators of ChinookMediaManager here.

How does it works?

The guywire project has an special “Output Path”:

image

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:

 

image

 

 

 

A last word about the Configurators

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:

image

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!


Posted Sat, 07 November 2009 09:12:52 AM by jfromainello
Filed under: NHibernate, WPF

comments powered by Disqus
© NHibernate Community 2024