How to register all implementations of an open generic interface using Autofac

2.5k Views Asked by At

I currently have an interface for a single step in a pipeline.

public interface IPipelineStep<T1, T2>
  where T1: ModelObject
  where T2: EntityObject { }

And I have a whole bunch of steps that implement this interface:

public class ValidateModelStep<T1, T2> : IPipelineStep<T1, T2>
  where T1: ModelObject
  where T2: EntityObject { }

public class Step2<T1, T2> : IPipelineStep<T1, T2>
  where T1: ModelObject
  where T2: EntityObject { }

public class Step3<T1, T2> : IPipelineStep<T1, T2>
  where T1: ModelObject
  where T2: EntityObject { }

public class Step4<T1, T2> : IPipelineStep<T1, T2>
  where T1: ModelObject
  where T2: EntityObject { }

I am currently registering them like this:

builder.RegisterGeneric(typeof(ValidateModelStep<,>)).As(typeof(IPipelineStep<,>)).AsSelf();
builder.RegisterGeneric(typeof(Step2<,>)).As(typeof(IPipelineStep<,>)).AsSelf();
builder.RegisterGeneric(typeof(Step3<,>)).As(typeof(IPipelineStep<,>)).AsSelf();
builder.RegisterGeneric(typeof(Step4<,>)).As(typeof(IPipelineStep<,>)).AsSelf();

And then I can use autofac to instantiate these steps. The problem is, I have many, many steps. And it's very frustrating to have to register each one every time I create a new one.

Is there any way to register them all at once?

I know you can use assembly scanning and AsClosedTypesOf, but this doesn't seem to work for open generic implementations of open generic interfaces.

Things I have tried:

builder.RegisterAssemblyTypes(myAssembly).AsClosedTypesOf(typeof(IPipelineStep<,>)).AsImplementedInterfaces();

builder.RegisterAssemblyTypes(myAssembly).AssignableTo(typeof(IPipelineStep<,>)).As(typeof(IPipelineStep<,>)).AsSelf();

builder.RegisterAssemblyTypes(myAssembly)
.Where(t => t.IsAssignableFrom(typeof(IPipelineStep<,>)))
.As(typeof(IPipelineStep<,>)).AsSelf();

Is there any way to use AsClosedTypesOf when the implementation of the interface must also contain generics?

Thanks in advance

2

There are 2 best solutions below

5
On BEST ANSWER

I suppose the most straightforward way is to just scan assembly yourself:

foreach (var t in myAssembly.GetTypes()
    .Where(c => !c.IsInterface && c.IsGenericTypeDefinition && c.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IPipelineStep<,>)))) {
    builder.RegisterGeneric(t).As(typeof(IPipelineStep<,>)).AsSelf();
}

This basically filters types which are open generic and implement IPipelineStep<>, then registers in container. I guess you can do similar thing with RegisterAssemblyTypes.Where(...) if you prefer.

0
On

The comment from the original post should probably be the accepted answer as this worked out perfect for a similar use case.

I wanted to register all implementations of my INotificationHandler<> interface. Using Autofac's assembly registration I was able to accomplish this.

builder.RegisterAssemblyTypes(assembly)
   .AsClosedTypesOf(typeof(INotificationHandler<>))
   .InstancePerDependency()
   .PreserveExistingDefaults();

By using the PreserveExistingDefaults each item get added to an IEnumerable<INotificationHandler<T>> and can be resolved is one big chunk or independently

using var scope = container.BeginLifetimeScope();
var handlers = scope
    .Resolve<IEnumerable<INotificationHandler<T>>>()
    .Select(h => h.HandleNotification(notification, cancellationToken));

The above code resolves all instances with the same Generic Type parameter. Both the concrete implementation and the first interface registered can and will be resolved.