Register container globally

Oct 14, 2013 at 2:04 AM
Hi all,

I'm pretty new using DI and simple injector (SI), and as I struggled I came to the following problem. I have a solution that introduces several projects, one referencing another and so on. As set in the SI examples, and as I read on the net, the initialization of the DI container should be set at the top level project (ex. global.asax or Main[]), from where I can reference and use all interfaces and classes needed to be instantiated (or services and components if preferred). Now, the problem is that I would like to use the same instance of that container in some of the lower level projects, so the SI can resolve the needed components over there.

My question is, if it is OK to create some static (global) var as the lowest level project in my solution which will create and hold the SI container, then, at app start, to call it from the global.asax and do the mappings between the services and the components, so finally to be able to use it throughout the complete solution as is?

Do you think this is a good approach, or somebody will recommend something else?

Thank you
Ilija
Coordinator
Oct 14, 2013 at 6:47 AM
Enabling global access to the container would be a bad idea. That would force your whole application to depend on the Simple Injector implementation, and defeats the purpose of dependency injecton. It will also kill testability.

It's hard to say what solution would be best for you, because it depends, but a general solution to this kind of problems is yo introduce an abstract factory: define an interface for the type you need to resolve half way and create an implementation of this factory inside your composition root (near your global.asax). This implementation is allowed to take a dependency on the container and call GetInstance on it.

If you postmore information about what you're trying to achieve, I could give more specific feedback.

Cheers
Developer
Oct 14, 2013 at 8:48 AM
Edited Oct 14, 2013 at 9:06 AM
@Ilija: The most extreme case of a 'factory interface' would probably be the CommonServiceLocator. Simple injector (as most other containers) provides an adapter for the CSL. But relying on a fully fledged dependency resolver should be considered carefully: In general the "service locator pattern" is considered to be harmful because it hides the dependencies of your components.
Maybe you are just looking for "modules" which can be as simple as static classes in your different assemblies with a static void RegisterServices(Container container) method? If you want to use a well-known Interface and use the auto-registration feature Simple injector has a "Packaging" extension module, see the discussion on SO.
(..) but a general solution to this kind of problems is yo introduce an abstract factory (..)
@Steve: BTW Have you considered adding automatic resolution of Func<T> and Lazy<T> Parameters in ctors? Currently with Simple Injector it is neccessary to explicitly register the Func<T> / Lazy<T> factory...
And regarding packages: What is your advice regarding dependencies to the DI container with packages? Is it best to create "micro-assemblies" which only contain the container-registration part for a module to avoid any assembly dependency to SimpleInjector in the modules?
Oct 14, 2013 at 11:03 AM
Edited Oct 14, 2013 at 11:04 AM
Hi guys,

Thanks for the reply. Let me try to be a bit more specific in what I'm trying to do. It is highly possible that I'm missing something or doing something wrong here.

In some extent I'm trying to follow this tutorial http://pluralsight.com/training/Courses/TableOfContents/building-multi-client-end-to-end-service-oriented-applications , but to extend it to support multiple databases, and to tweak and customize it to fit my project needs (this tutorial uses MEF, but I don't think that the different type of DI tool is the issue here). In short, following is what I'm trying to accomplish:
  1. I need to have multiple types of repositories (ex. MongoDB, MS SQL and others) that all derive from same interface ex. IDataRepository that introduces simple CRUD functionality.
  2. Above these repositories, I have entities like Account, Application etc. and implementation of AccountRepository, ApplicationRepository etc that should also implement the same CRUD operations since they would inherit from same IDataRepository, and give possibility to extend those
  3. In between, I'm creating a DbContext class and accompanying interface to it, that the above repositories would also implement, and this implementation is responsible of defining whether the AccountRepository will use MongoDb or MS SQL.
  4. On top of these services, there will be some Domain (or Manager) layer, that will do the business logic. Around this layer I am having Validator service, that should validate all incoming requests from the Web API before they hit the Manager.
  5. Ofcourse there are some other services like Logger, that is used throughout all layers.
All of the modules mentioned above are actually separate Visual Studio projects of the same solution, so you can recognize the hierarchy.

I wanted to use some factory pattern implementation to encapsulate the instantiation of the repositories, and these factory classes to reside somewhere in the repositories project or as a layer above them. Having it done this way, I should do something like:
public static IRepository<TEntity> GetRepository<TEntity>()
{
      return container.GetInstance<IRepository<TEntity>>();
}
but the problem here is that I don't have an instance of the SI "container" class, since the SI "container" class is defined and instantiated at the very beginning of project in the global.asax file (several levels above).

Further more, I would like to create some custom Test classes for some/each of the projects described above, that i can use the DI container to mock the data and test the implementation.

Regards,
Ilija
Developer
Oct 14, 2013 at 11:33 AM
Just some more questions for clarification.
  1. I need to have multiple types of repositories (ex. MongoDB, MS SQL and others) that all derive from same interface ex. IDataRepository that introduces simple CRUD functionality.
Is the db access layer configured at application start-up time and you are only using a single variant (e.g. MongoDB OR SQL Server) or are multiple different stores used at the same time? What has to be actually configured/selected at runtime?
I wanted to use some factory pattern implementation to encapsulate the instantiation of the repositories, and these factory classes to reside somewhere in the repositories project or as a layer above them. Having it done this way, I should do something like:
Is there a reason why classic ctor-injection of the repository is not possible in your case? I would like to understand why you need the explicit factory. Normally the construction/selection of the repository can be handed out to the container e.g. you set up WebAPI to use Simple Injector and have a ctor in your controller that has an argument of type IRepository<TEntity>. If you are only using a single db-store at a time you can configure the concrete impl that implements the repository interface in your composition root.
Oct 14, 2013 at 1:41 PM
Every entity repository instance is configured during the development and it will use only one of the provided databases, for example we know that AccountRepository will ONLY use MongoDb. It is just that the connection string will be set during the runtime, for example if ApplicationId = 1, then I will create the mongodb conn string to take the Accounts from database name "1", collection name "Accounts", and ApplicationId will be set according the url subdomain (ex: mongodb://127.0.0.1/1/Accounts ). So, the conn string is NOT configured in the web.config or similar.

Regarding the classic ctor-injection, probably what you suggest will do. Nevertheless, just to explain my idea:

I am extracting each Repository class through their own interface, example for AccountRepository, I have IAccountRepository where I implement additional stuff like GetAccountByEmail. The basic idea was, all of these Repository classes to be instantiated through one factory method and to be able to do something similar to following:
IAccountRepository accRepo = RepositoryFactory.GetRepository<IAccountRepository>();

and then

var acc = accRepo.GetAccountByEmail("my@mail.com");
Ilija
Coordinator
Oct 14, 2013 at 8:21 PM
I like to warn you about a design where each repository has its own interface. Prefer having a single IRepository<T> abstraction and don't try to add custom query methods on it, but create separate classes for each query. I've written an article about this, which I think you should read. The applications are write use this pattern extensively.

I do agree with @blueling btw about injecting repositories into the constructor of consumers. This allows your object graph to be verifiable. Nonetheless, having such factory starts to get interesting when you're writing some peace of business logic that needs to interact with multiple entity types.

This repository factory of yours btw is actually a common design pattern, namely the Unit of Work. And do note that here as well, having an IRepository<T> interface, makes your Unit of Work implementation much simpler. For instance:
public interface IUnitOfWork
{
    IRepository<T> GetRepository<T>() where T : IEntity
    void Commit();
}
If you wish, you could even create handy extension method to prevent having to call the Repository method:
public static UnitOfWorkExtensions
{
    public static IRepistory<Customer> Customers(this IUnitOfWork uow)
    {
        return uow.GetRepository<Customer>();
    }

    public static IRepistory<Order> Orders(this IUnitOfWork uow)
    {
        return uow.GetRepository<Order>();
    }
}
Marked as answer by dot_NET_Junkie on 3/2/2014 at 11:14 AM
Coordinator
Oct 14, 2013 at 8:23 PM
@blueling: I moved your questions to a new thread.
Coordinator
Oct 14, 2013 at 9:37 PM
@blueling: I alo moved your other question to this new thread.
Oct 15, 2013 at 8:45 AM
Hi guys,

It look like my problem is a bit wider than the SI, so I will try to reconfigure some of the stuff. Nevertheless, I would like to thank you for the help.

Regards,
Ilija