0
votes

Note: Just to clarify, this question is not about setting up IoC in MVC controllers but outside them.

I have IoC working fine in my controllers however I need to have it working outside the controller in custom classes as well (in a CacheManager class which uses a list of CacheBuilders to build the cache).

For that until now I was passing a reference of the Windsor container to the CacheManager and the CacheManager would pass it down to each CacheBuilder to resolve any references it might need to resolve. However I've believe that isn't a good idea and I want to do that without passing in this reference and from what I read, I think typed factories can help me with that.

I created an interface like below...

public interface ITypeFactory
{
    T Create<T>();
    void Release(object value);
}

... and registered it as a type for a factory in the BootStrapContainer method which gets fired when the web app starts....

 container.AddFacility<TypedFactoryFacility>();
 container.Register(Component.For<ITypeFactory>().AsFactory());

Now I intend to use this as the following in one of my CacheBuilders...

public class CountryCacheBuilder : ICacheBuilder
{
    public ITypeFactory TypeFactory { get; set; }

    public void PersistToCache(Cache cache)
    {
        var repository = TypeFactory.Create<ICountryRepository>();

        var listOfCountries = repository.GetAllCountries();

        // add list of Countries to cache
    }
}

... given ICountryRepository is defined as below....

public interface ICountryRepository
{
    List<Country> GetAllCountries();
}

... and in Global.asax App_Start method I register all CacheBuilders with the CacheManager like so ...

CacheManager.Instance.CacheBuilders.Add(new CountryCacheBuilder());

and then call

CacheManager.BuildCache()

which just iterates over the list of CacheBuilders and fires PersistToCache for each of them.

However in CountryCacheBuilder.PersistToCache method I see that the the TypeFactory property is null when I was expecting it to be instantiated. Can someone tell me what am I missing here ? Do I need to tell the container how create each and every CacheBuilder ?

1

1 Answers

1
votes

I was passing a reference of the Windsor container to the CacheManager ... I've believe that isn't a good idea

You are right; this isn't a good idea. You are basically applying the Service Locator anti-pattern.

I created an interface like below...

You simply created an abstraction for your IoC container, which is still an implementation of the Service Locator anti-pattern. In other words, still a bad idea.

Instead of injecting the ITypeFactory, you should simply inject the dependencies a class needs. Further more, you shouldn't use property injection, because Castle Windsor will still create a class for you, even if its dependencies couldn't be resolved (it simply skips properties that can't be injected). This sounds nice, but you rather want your application to fail fast, instead of allowing the container to return an instance that can't be used (since most dependencies are typically required for the class to work).

This is how you CountryCacheBuilder should look like:

public class CountryCacheBuilder : ICacheBuilder
{
    private readonly ICountryRepository repository;

    public CountryCacheBuilder(ICountryRepository repository)
    {
        this.repository = repository;
    }

    public void PersistToCache(Cache cache)
    {
        var listOfCountries = this.repository.GetAllCountries();

        // add list of Countries to cache
    }
}

Castle Windsor (and any other IoC container for that matter) will analyse the class's constructor and inject its dependencies. And not only should you apply the constructor injection pattern for this type, but also for its dependencies and its consumers. Your controller for instance, might look like this:

public class CountryController : Controller
{
    private readonly ICacheBuilder cacheBuilder;

    public CountryController(ICacheBuilder cacheBuilder)
    {
        this.cacheBuilder = cacheBuilder;
    }

    public ActionResult Index()
    {
        // ...
    }
}

By default however, MVC will create controller classes for you, not Castle. You will have to redirect the creation to Castle.