Search code examples
c#autofactyped-factory-facility

Autofac and Generic Typed Factory like Castle Windsor


In the current project I am working with Autofac but usually I use Castle Windsor, so I am trying to understand how I can resolve the typed factory pattern available in Castle Windsor, by using Autofac.

I have the following Query and Query Handler contract:

public interface IQuery<out TResult>
{

}

public interface IQueryHandler<in TQuery, out TResult> where TQuery : IQuery<TResult>
{
    TResult Handle(TQuery query);
}

Each contract has multiple implementations, so for each IQuery<result> there is a corresponding IQueryHandler<IQuery<result>, result> implementation.

I am registering all using this fluent approach:

public class CqrsModule : Module
{
    /// <inheritdoc />
    protected override void Load(ContainerBuilder builder)
    {
        // prepare all assemblies for IoC
        var assemblies = new[]
        {
            typeof(IQuery<>).Assembly,
            typeof(IQueryHandler<,>).Assembly,
            typeof(IBus).Assembly
        };

        // autofac regisration
        builder.RegisterAssemblyTypes(assemblies)
            .AsClosedTypesOf(typeof(IQuery<>))
            .AsClosedTypesOf(typeof(IQueryHandler<,>))
            .AsClosedTypesOf(typeof(IBus));
    }
}

But I have a problem. From my Bus I do not want to be aware of the existing Handlers, so I use a Typed Factory as following:

public interface IQueryFactory
{
    IQueryHandler<TQuery, TResult> BuildHandler<TQuery, TResult>() 
    where TQuery : IQuery<TResult>;
}

But I have no idea how I can register this Typed Factory using Autofac. In Castle Windsor I would simply do:

kernel.Register(
    Component.For<IQueryFactory>()
        .AsFactory()
);

Solution

  • Apparently the answer is no, because Autofac does not have such a concept of Typed Factory.

    The alternative is to keep the contract and just create an implementation of it for the specific case scenario which is Use typed factory in Autofac.

    public sealed class AutofacQueryFactory : IQueryFactory
    {
        private readonly ILifetimeScope scope;
    
        public AutofacQueryFactory(ILifetimeScope scope)
        {
            this.scope = scope;
        }
    
        /// <see cref="IQueryFactory" />
        public IQueryHandler<TQuery, TResult> BuildHandler<TQuery, TResult>() where TQuery : IQuery<TResult>
        {
            return this.scope.Resolve<IQueryHandler<TQuery, TResult>>();
        }
    }
    

    Note You should inject ILifetimeScopeand not IContainer according to Autofac documentation.