I'm trying to write my composition root in a future proof way so that when I add more commands that implement base entity with SimpleInjector 4 they automatically get picked up.
Domain classes look like this:
public interface ICommandHandler<TCommand> { . . . }
public class UpdateCommandHandler<T> : ICommandHandler<UpdateCommand<T>>
where T : EntityBase { . . . }
public class UpdateCommand<T> where T : EntityBase { . . . }
public abstract class EntityBase { . . . }
My API controllers then use command handlers as follows:
// With base entity
private ICommandHandler<UpdateCommand<BlogEntry>> updateBlogEntryCommandHandler;
// Without base entity
private ICommandHandler<AddBlogEntryCommentCommand> addBlogEntryCommentCommandHandler;
And my composition root looks like this:
// This works for all cases without as base entity e.g:
// private ICommandHandler<AddBlogEntryCommentCommand> addBlogEntryCommentCommandHandler;
container.Register(typeof(ICommandHandler<>), new[] { typeof(ICommandHandler<>).Assembly });
// And this works for when commands have a base entity but how do to I write it in a
// generic, future-proof way like above?
container.Register<ICommandHandler<DeleteCommand<BlogEntryComment>>, DeleteCommandHandler<BlogEntryComment>>();
container.Register<ICommandHandler<UpdateCommand<BlogEntry>>, UpdateCommandHandler<BlogEntry>>();
container.Register<ICommandHandler<UpdateCommand<BlogEntryFile>>, UpdateCommandHandler<BlogEntryFile>>();
Note: this is a similar to Simple Injector usage for generic command handler but it does not answer my question.
Instead of making all registrations for the closed-generic versions of your open-generic implementations one by one, you can register them with their open-generic types instead:
container.Register(typeof(ICommandHandler<>), typeof(DeleteCommandHandler<>));
container.Register(typeof(ICommandHandler<>), typeof(UpdateCommandHandler<>));
Simple Injector will automatically close those types for you. Only when a new generic implementation is added, you will have to add it as registration to yuor Composition Root.
If you expect many generic types to be added, you can instead register all implementations at once using the following code:
var types = container.GetTypesToRegister(typeof(ICommandHandler<>),
new[] { typeof(ICommandHandler<>).Assembly },
new TypesToRegisterOptions { IncludeGenericTypeDefinitions = true });
container.Register(typeof(ICommandHandler<>),
types.Where(t => !t.IsGenericTypeDefinition));
foreach (Type type in types.Where(t => t.IsGenericTypeDefinition))
{
container.Register(typeof(ICommandHandler<>), type);
}
Due to a limitation Register
in v4.0 (that is planned to get fixed in v4.1), Register(Type, IEnumerable<Type>)
does not allow open-generic types to be passed in to the list of types. That's why you'll have to register the generic types separately (for now).