Search code examples
c#dependency-injectionautofac

Autofac provide func to create dynamic factory


I would like to create a dynamic factory in AutoFac. Let me outline my problem, I'm trying to register Adapters with my Factory:

Note: Due to legacy code I cannot modify the architecture of the Internal adapters.

protected void RegisterAdaptables(ContainerBuilder builder)
{
    this.RegisterAdaptableInstance
        <IOnboardingTask, OnboardingTaskDTO, IToDTOAdapterOptions<IOnboardingTask>>(
             builder,
             options => new OnboardingTaskGetterAdapter(options.Entity).ToDTO());
}

protected void RegisterAdaptableInstance<TEntity, TDTO, TOptions>(
    ContainerBuilder builder, Func<TOptions, TDTO> adaptFunc)
    where TOptions : IToDTOAdapterOptions<TEntity>
{
    var instance = new ToDTOAdaptable<TEntity, TDTO, TOptions>(adaptFunc);
    builder.RegisterInstance(instance)
        .As<IToDTOAdaptable<TEntity, TDTO, IToDTOAdapterOptions<TEntity>>>();
}

The ToDTOAdaptable class is just a wrapper around a Func which provider a way to Adapt From class A to class B, (Note: this is done this way so we can instantiate new adapters from our singleton services)

Occasionally my Internal Adapters need Dependencies that are provided in the container.

I would like to register a dynamic Factory so I can consume the IContainer from the registration.

I would like to be able to register an Adaptable, and obtain additional dependencies from the container e.g

builder.RegisterFactory(IContainer container => { 
    return new ToDTOAdaptable(options => {
         var context = container.Resolve<MyDBContext>();
         return new OnboardingTaskGetterAdapter(context, options.Entity).ToDTO();
    });
}).As<IToDTOAdaptable<TEntity, TDTO, IToDTOAdapterOptions<TEntity>>>();

Solution

  • You can change your RegisterAdaptableInstance method to accept a Func with the autofac component context. Then rather than registering an instance to the container you would register a function that will instantiate it where it has access to the context.

        protected void RegisterAdaptables(ContainerBuilder builder)
        {
            this.RegisterAdaptableInstance<IOnboardingTask, OnboardingTaskDTO, IToDTOAdapterOptions<IOnboardingTask>>(
                    builder, 
                    scope => 
                    {
                        options =>
                        {
                            var context = scope.Resolve<MyDBContext>();
                            return new OnboardingTaskGetterAdapter(context, options.Entity).ToDTO();
                        }
                    });
        }
    
        protected void RegisterAdaptableInstance<TEntity, TDTO, TOptions>(ContainerBuilder builder, Func<IComponentContext, Func<TOptions, TDTO>> getAdaptFunc)
            where TOptions : IToDTOAdapterOptions<TEntity>
        {
            builder
                .Register(c => new ToDTOAdaptable<TEntity, TDTO, TOptions>(getAdaptFunc(c)))
                .As<IToDTOAdaptable<TEntity, TDTO, IToDTOAdapterOptions<TEntity>>>();
        }