Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Bricks and Mortar: Building a Castle : Page 2

Discover how to use the Castle Windsor Inversion of Control container to build flexible applications.


advertisement

Getting Objects Out of the Container

In this section, you'll look at the various ways to get objects out of the container.

Hey, Container, I Need an IAccountsPayable

The most common container lookup asks the container for the implementer of a particular interface:



var ap = IoC.Resolve<IAccountsPayable>();

You ask the container for the implementer of IAccountsPayable and it returns an object that implements the requested interface. The returned object is ready to perform work for you when you call its methods/properties. Any of AccountsPayable's dependencies have been resolved and satisfied before the container returns it to you.

If multiple implementations of IAccountsPayable have been registered in the container, Castle Windsor returns the first one registered.

Author's Note: This is an area where containers differ substantially. Some containers throw an exception if multiple implementations have been registered whereas others return the last implementation registered. Read the container documentation carefully if you are contemplating switching from one IoC container to another and are dependent on this override behavior.

In the Key of T

Let's say you are integrating multiple databases and access them via IDbGateway implementations. You have an IDbGateway per database registered in the IoC container. How do you get the specific IDbGateway that you are looking for? If you need a specific implementation, you can request it by string-based key:

var gateway = IoC.Resolve<IDbGateway>(DatabaseKey.Reporting);

In this case, DatabaseKey.Reporting is simply a string constant, "reporting." You can now use this specific implementation to access the Reporting database via the IDbGateway object.

Getting Greedy

Suppose you want all the implementations of a particular interface? For example, I want all registered validators for invoices:

var validators = IoC.Resolve<IValidator<Invoice>>();

Notice that Castle Windsor can resolve generic interfaces and implementations. You get back all types that are registered as implementing IValidator<Invoice>, but not implementers of IValidator<Customer>. This lets you to write very general code that can validate invoices without tying yourself to a particular set of validation rules. Some of these validation rules may actually be added at run time after the application is deployed!

I Have Something You Might Need

Sometimes you need to supply an externally created instance as a constructor argument. For example, the ASP.NET pipeline creates derived instances of Page. Another example would be WPF creating instances of XAML Pages/Windows. These are your views, which are a dependency of your Presenters/ViewModels. Windsor allows you to supply externally created dependencies as constructor arguments. For example, in the ASPX page's constructor, you would call:

presenter = PresenterFactory.Create <AccountsPayablePresenter>(this);

Notice that you are passing "this" as the view to the PresenterFactory because you want to wire the Presenter to the view instance (ASPX page) created by the ASP.NET pipeline:

public class PresenterFactory {    public static T Create<T>(IView someView) {     return IoC.Resolve<T>( new { view = someView });     } }

You construct an anonymous type with a property named view, which matches the name of the constructor argument that you want to supply. You could have also used an IDictionary where the key is the name of the constructor argument. When satisfying dependencies, Windsor will use any registered dependencies as well as any supplied during the call to Resolve<T>() to construct an instance of the desired type.

Author's Note: Whether you use anonymous types or keys in an IDictionary to supply externally created constructor arguments, it is your responsibility to ensure that the properties/keys match the names of the constructor arguments. If the constructor arguments are renamed, the corresponding anonymous type property/IDictionary key will not be renamed. Tests for the PresenterFactory (and similar classes) can verify that names match properly.

Getting Dependencies In

You know how to get fully constructed objects out, but how do you get all the dependencies into an IoC container so the container can do its job of constructing objects? As an example, the AccountsPayable class needs to collaborate with some other objects to get its job done, namely an authorization service, an invoice validator, and an invoice repository. You define your list of dependencies using the AccountsPayable constructor:

public AccountsPayable( IAuthorizationService authService, IValidator<Invoice> invoiceValidator, IInvoiceRepository invoiceRepository) {     this.authorizationService = authService;     this.invoiceValidator = invoiceValidator;     this.invoiceRepository = invoiceRepository; }

This is known as constructor injection. The IoC container will create an instance of AccountsPayable using the above constructor supplying it with instances of the appropriate dependent objects.

Author's Note: Many IoC containers support setter injection as well as constructor injection. With setter injection, the IoC container uses property setters rather than constructor parameters to supply dependencies, which is useful for optional dependencies. However, you should prefer constructor injection, because it guarantees that your object has all of its required dependencies satisfied, resulting in a lot less null checking. Many popular containers let you freely mix constructor and setter injection as needed.

Note that if any of AccountsPayable's dependencies had dependencies themselves, the IoC container would find and supply those as well. When wiring dependencies, the container walks the entire dependency graph and creates the dependencies in order from least dependent to most dependent. If it finds a dependency cycle (such as A depends on B depends on A), Castle Windsor throws an exception.

The Road to Heck Is Paved in Angle Brackets

When Castle Windsor was first introduced, its only means of configuration was XML files. You define a list of components in a file typically called Windsor.config. A component implements a service (aka interface or abstract base class) and is implemented by a concrete type (aka class). Let's take a look at a simple example below:

IoC Containers perform two basic functions: putting dependencies in and getting fully constructed objects out.

<?xml version="1.0" encoding="utf-8" ?> <configuration> <components> <component id="AccountsPayable" service="JamesKovacs.BricksAndMortar .Services.IAccountsPayable, JamesKovacs.BricksAndMortar" type="JamesKovacs.BricksAndMortar .Services.AccountsPayable, JamesKovacs.BricksAndMortar"/> </components> </configuration>

XML configuration suffers from a number of problems:

  1. Adding a new component requires updating Windsor.config.
  2. Configuration errors aren't caught until run time.
  3. If you refactor the name of a class or interface or move it to a new namespace or assembly, you have to update Windsor.config appropriately.
  4. Configuring generic services/components requires the CLR generics syntax (e.g., to configure a service, IValidator, you need to use the full CLR type name:

JamesKovacs.BricksAndMortar.Services.IValidator1[ [JamesKovacs.BricksAndMortar.Domain.Invoice, JamesKovacs.BricksAndMortar]], JamesKovacs.BricksAndMortar.

Windsor.config is typically quite brittle and can become large quickly as you register all your components.

Parlez-vous C#?

The Castle Windsor 2.0 RTM contains another option: fluent configuration in C#. The concept was borrowed from another popular IoC container, StructureMap. Rather than baking IoC configuration in refactoring-hostile XML files, you perform the configuration in C# code. For example, the following C# is equivalent to the preceding XML:

container.Register(   Component.For<IAccountsPayable>() .ImplementedBy<AccountsPayable>() );

That solves some of the problems with XML configuration, namely #2, #3, and #4 above. Configuration errors, such as misspelled class/interface names, are now compile-time errors. Because this is C#, refactoring tools can now modify class/interface names, namespaces, and assemblies appropriately when they change. Lastly, you can use C# syntax for generics, which you will see more of later.

However, it still doesn't solve problem #1, which is that adding a new component requires updating the configuration code, which in this case is in the bootstrapper.

It also introduces a limitation, which is that configuration is now baked into the assembly, thus you must recompile the application to make a configuration change. There are a few solutions to this problem. One is to simply accept the limitation (which is potentially a benefit). Do you really want your administrator changing your IInvoiceRepository implementation? Quite honestly, a lot of dependencies should be baked into the application. The more knobs there are available to turn in configuration files, the more likely that your application will be mis-configured. So accept that you want a well-decoupled application so that maintenance/modification is easier, but you don't want to enable this at run time.

For the few dependencies that do need to be tweaked at run time, you can create a hybrid approach that combines both XML and fluent configuration. You'll see more about this option later in this article.



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap
Thanks for your registration, follow us on our social networks to keep up-to-date