In our last post we implemented a controller which grabbed a list of books and passed them over to the view to display them. In this post we are going to move that grabbing of a list of books into something else and see how WIndsor can help us out.
So our current list of books is retrieved as below:
We would like to refactor this so that the controller wasn’t responsible for getting this list directly but instead worked with something else to get this listing. My preferred way would be to access this list from a BookRepository like so:
But where do we get bookRespository from? One way would be to new up an instance directly but doing so would hide the dependency that BookController has on BookRepository. But here we can lean on the WIndsor integration and use the Dependency Injection pattern to push our dependencies into our controller (while at the same time making it clear that our controller has a dependency on BookRepository).
Our first step is to create our new BookRepository and move our current book listing to it. It doesn’t really fit within any of the structure the Monorail wizard generated for us (although Models would be the best match) so I’m creating it under a newly added Services folder.
Now we can add a constructor to our controller that caches the passed in BookRepository instance like so:
So now we have moved our book retrieval out into its own class and made our dependency on this new class explicit via our constructor in the controller. If we hit F5 and view our page we get a “Can’t create component ‘book.controller’ as it has dependencies to be satisfied.” error like below:
The reason for this is that, as it says, the BookController depends on BookRepository which was not registered. This is because when Monorail invokes our BookController it does so through Windsor which then resolves our dependencies for us.
Registering our BookRepository is handled in a similar way to which we registered our controller by adding our new BookRepository to components.config in the config folder. All dependencies that aren’t controllers will need to be registered in this file.
If we open up components.config you will see an empty components element. In this we add a component node for our new BookRepository:
<component id="bookRepository" type="BookLendingService.Services.BookRepository, BookLendingService" />
Now that we have registered both our component and controller then Windsor can resolve our dependencies for us. So if we hit F5 and view our page we should now see the exact same page we had before we moved the book listing logic.
So we can start to see where the Windsor integration allows us to benefit from dependency injection. As another example we should really introduce an interface for our BookRepository and get the controller to depend on this rather than on the implementation. This allows us to easily mock our dependencies when we test our controllers.
To do this, first extract the interface from our BookRepository (with it’s only member at the moment) and then change BookRepository to implement the interface. Now update BookController to change all uses of BookRepository to IBookRepository, and finally, a change to the components.config file to add a service attribute:
<component id="bookRepository" service="BookLendingService.Services.IBookRepository, BookLendingService" type="BookLendingService.Services.BookRepository, BookLendingService" />
If you build and view the page again then we should still see our list of books as before but now we are depending on interfaces rather than our implementations.
In our next post we will lean on Windsor even more by enabling auto registration of our controllers and components/services. To do this we will also need to upgrade our Monorail assemblies to a later version which we will also cover.