Question

I would like to make a keyedService based on the T for my Open Generic ICommandHandler. I would like to register a ConsultatCommandHanlder keyed service when the ICommandHandler has a T that inherits from ConsultantCommand

Any idea how to do it? Or if it is even possible? I am new to AutoFac and am struggling.

I am presently registering CommandHandler like this:

        //Register All Command Handlers
        builder.RegisterAssemblyTypes(assemblies)
          .As(
              t =>
              t.GetInterfaces()
               .Where(a => a.IsClosedTypeOf(typeof (ICommandHandler<>)))
               .Select(a => new KeyedService("commandHandler", a))).InstancePerHttpRequest();

If it is possible I'd guess I have to identify the CommandHandlers when I get the Closed Type and in some way identify the ones where the Command implements ConsultantCommand.

I tried:

        builder.RegisterAssemblyTypes(assemblies)
               .As(
                 t =>
                   t.GetInterfaces()
                    .Where(a => a.IsClosedTypeOf(typeof(ICommandHandler<>)) &&
                        a.GetGenericArguments()[0].IsSubclassOf(typeof(ConsultantCommand)))
                    .Select(a => new KeyedService("ConsultantCommandHandler", a))).InstancePerHttpRequest();

But not joy doesn't seem to work. It compiles but now no CommandHandlers are registered even those that do inherit from ConsultantCommand. I think my syntax is all wrong

Was it helpful?

Solution

First, you'll need to make sure your ICommandHandler<T> is declared to support covariance:

public interface ICommandHandler<out T> { }

That out is important or you won't be able to resolve all the ConsultantCommand handlers at once. You'll also get an Autofac exception.

Next, use the Named extension method to register your named service rather than doing it yourself. The syntax will look something like this:

builder.RegisterAssemblyTypes(assemblies)
       .Where(t =>
                t.IsClosedTypeOf(typeof(ICommandHandler<>)) &&
                t.GetInterfaces()[0]
                 .GetGenericArguments()[0]
                 .IsAssignableTo<ConsultantCommand>())
       .Named("name", typeof(ICommandHandler<ConsultantCommand>))
       .InstancePerHttpRequest();

That registers all of the services that are ICommandHandler<T> where T derives from ConsultantCommand as an ICommandHandler<ConsultantCommand>. You have to use the base type or, again, you won't be able to resolve all the handlers at once. There isn't any method to "resolve all the services that derive from this base type." There's also no way to resolve a list of open generics.

When you resolve the list of handlers, you'll need to resolve a named IEnumerable<T>:

using(var scope = container.BeginLifetimeScope())
{
  var x =
    scope.ResolveNamed<IEnumerable<ICommandHandler<ConsultantCommand>>>("name");
}

Of course, you're using InstancePerHttpRequest so it'd be more like:

var x =
  AutofacDependencyResolver
    .Current
    .RequestLifetimeScope
    .ResolveNamed<IEnumerable<ICommandHandler<ConsultantCommand>>>("name");

As mentioned above, you have to register as a closed generic because this won't work:

// WON'T WORK:
scope.ResolveNamed<IEnumerable<ICommandHandler<>>>("name");

You can modify the registration and such as needed. The rest of the registration extensions should work as usual - if you want to register things as implemented interfaces or whatever else, it should work with RegisterAssemblyTypes just like you were doing it with a single service.

OTHER TIPS

You want:

builder.RegisterAssemblyTypes(assemblies)
       .AsClosedTypesOf(typeof(ICommandHandler<>))
       .AsSelf()
       .AsImplementedInterfaces()
       .InstancePerHttpRequest();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top