Search code examples
c#.netasp.net-mvcdependency-injectionsimple-injector

Register instance creator for generic type with Simple Injector


I have the following configuration in Simple Injector:

c.Register<IQueryable<Entity1>>(() => c.GetInstance<IDomainQueryFactory>().Query<Entity1>());
c.Register<IQueryable<Entity2>>(() => c.GetInstance<IDomainQueryFactory>().Query<Entity2>());
c.Register<IQueryable<Entity3>>(() => c.GetInstance<IDomainQueryFactory>().Query<Entity3>());
c.Register<IQueryable<Entity4>>(() => c.GetInstance<IDomainQueryFactory>().Query<Entity4>());
...

The IDomainQueryFactory factory is responsible for creating the instances of IQueryable<TEntity>. Some class (like MVC controller) then declares dependency only on IQueryable<Entity1>.

I am wondering if there is some way of writing these registrations with a single command.


Solution

  • There is no way to do this, because Simple Injector favors composition using types over functional composition. This means that Simple Injector does not easily allow you to register a generic method to be used in the registration process, just as you would be able to use a generic type.

    So to reduce the amount of boilerplate code and prevent having to update the configuration on each new entity, you will have to create a generic IQueryable<T> implementation and register that. This is how this implementation would look like:

    public sealed class DomainQueryFactoryQueryable<T> : IQueryable<T>
    {
        private readonly IDomainQueryFactory factory;
    
        public DomainQueryFactoryQueryable(IDomainQueryFactory factory) {
            this.factory = factory;
        }
    
        public Type ElementType { get { return this.GetQuery().ElementType; } }
        public Expression Expression { get { return this.GetQuery().Expression; } }
        public IQueryProvider Provider { get { return this.GetQuery().Provider; } }
    
        public IEnumerator<T> GetEnumerator() {
            return this.GetQuery().GetEnumerator();
        }
    
        IEnumerator IEnumerable.GetEnumerator() {
            return this.GetEnumerator();
        }
    
        private IQueryable<T> GetQuery() { 
            return this.factory.Query<T>();
        }
    }
    

    And you can register this as follows:

    container.RegisterOpenGeneric(typeof(IQueryable<>), typeof(DomainQueryFactoryQueryable<>));