Reputation: 496
I have a problem of forgetting to register my services. I need a mechanism that the app would automatically find and register all my services based on a marker interfaces.
I wrote this code, which works fine:
public interface IService { }
public interface ISingletonService : IService { }
public interface IScopedService : IService { }
public interface ITransientService : IService { }
...
public static IEnumerable<T> TapWhere<T>(
this IEnumerable<T> source,
Func<T, bool> predicate,
Action<T> action)
{
foreach (var item in source)
{
if (predicate(item))
{
action(item);
}
yield return item;
}
}
...
public static WebApplicationBuilder AddApplicationServices(this WebApplicationBuilder builder)
{
var firstLevelAssemblies = Assembly.GetEntryAssembly()!
.GetReferencedAssemblies()
.Select(Assembly.Load);
var secondLevelAssemblies = firstLevelAssemblies
.SelectMany(x => x.GetReferencedAssemblies())
.Select(Assembly.Load);
_ = firstLevelAssemblies.Concat(secondLevelAssemblies)
.DistinctBy(x => x.FullName)
.SelectMany(x => x.GetTypes())
.TapWhere(
typeof(ISingletonService).IsAssignableFrom,
x => builder.Services.AddSingleton(x))
.TapWhere(
typeof(IScopedService).IsAssignableFrom,
x => builder.Services.AddScoped(x))
.TapWhere(
typeof(ITransientService).IsAssignableFrom,
x => builder.Services.AddTransient(x))
.ToArray();
return builder;
}
I wanted to do the same with my configuration files. But I got stuck because unlike service registrations, the Configure<>
method is generic and doesn't accept a Type
instance.
public interface IConfig { }
...
_ = firstLevelAssemblies.Concat(secondLevelAssemblies)
.DistinctBy(x => x.FullName)
.SelectMany(x => x.GetTypes())
.TapWhere(
typeof(IConfig).IsAssignableFrom,
x => builder.Services.Configure<>()) // :(
.ToArray();
Is there any way I can automatically register my config files based on a marker interface?
Upvotes: 0
Views: 44