3

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

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Aaron
  • 282
  • 1
  • 4
  • 11
  • 1
    Have you tried `builder.RegisterAssemblyTypes(myAssembly).AsClosedTypesOf(typeof(IPipelineStep<,>))` without calling `AsImplementedInterfaces()`? – vendettamit May 23 '18 at 21:53
  • I did try that and got the same result. Tried that first, added AsImplementedInterfaces() after since I found someone else who said that worked for them. The problem is that their classes did not have generics in them, but implemented an interface by passing in Hard coded generic types. That is the only difference I can see, and I assume that is why it works for them and not for me. This is the thread: https://stackoverflow.com/questions/16757945/how-to-register-many-for-open-generic-in-autofac – Aaron May 23 '18 at 22:23

2 Answers2

4

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.

Evk
  • 98,527
  • 8
  • 141
  • 191
  • Thanks for your answer. I actually did try using `RegisterAssemblyTypes.Where(...)` before posting here. Edited my post above to reflect that. But I also tried this solution (had to add a check for `x.IsGenericTypeDefinition` in order to call `x.GetGenericTypeDefinition()`) and put a breakpoint in the foreach loop. Nothing meets the criteria of the where clause and nothing is registered using that method. I'm going to play around with that a little more to see if I can get it working though. Thank you. – Aaron May 23 '18 at 22:52
  • That's strange, because I tested this on simple type implementing your (empty) IPipelineInterface. – Evk May 23 '18 at 22:54
  • Hmm, interesting. My interface is actually a bit more complicated than what I posted in the example, but I did not think that would make a difference. But I must be doing something differently than what I posted. For one thing, my `IPipelineStep<>` interface actually implements another base interface with it's own set of generics. But I did not think that would make a difference. I'm going to keep playing around. – Aaron May 23 '18 at 22:58
  • Okay I got this to work, thank you very much. My first attempt simply had a bug in it, I tried `x.IsGenericTypeDefinition` instead of `x.IsGenericType` I had to change the where clause of the Any statement to read: `c.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericInterfaceType)` – Aaron May 23 '18 at 23:16
  • This would not work without first checking if the interface was a generic type, as you can not call GetGenericTypeDefinition on a non-generic type. – Aaron May 23 '18 at 23:17
0

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.