I guess you like the way it works with Autofac:
var assembly = typeof(MyModule).Assembly;
builder.RegisterAssemblyTypes(assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
But you don't want to switch to Autofac for some reasons (for example you want use extensions from external libraries to register their dependencies).
So my suggestion is to create some extensions that use reflection like these ones:
public static IServiceCollection AddSingletonsByConvention(this IServiceCollection services, Assembly assembly, Func<Type, bool> interfacePredicate, Func<Type, bool> implementationPredicate)
{
var interfaces = assembly.ExportedTypes
.Where(x => x.IsInterface && interfacePredicate(x))
.ToList();
var implementations = assembly.ExportedTypes
.Where(x => !x.IsInterface && !x.IsAbstract && implementationPredicate(x))
.ToList();
foreach (var @interface in interfaces)
{
var implementation = implementations.FirstOrDefault(x => @interface.IsAssignableFrom(x));
if (implementation == null) continue;
services.AddSingleton(@interface, implementation);
}
return services;
}
public static IServiceCollection AddSingletonsByConvention(this IServiceCollection services, Assembly assembly, Func<Type, bool> predicate)
=> services.AddSingletonsByConvention(assembly, predicate, predicate);
Now you can register all your services by simple code like this:
var assembly = typeof(MyType).Assembly;
services.AddSingletonsByConvention(assembly, x => x.Name.EndsWith("Service"));
Feel free to customize these extensions to match your needs.
For example you can fire an exception if you don't find implementations for some services if that will make you feel a bit safer.