Can .NET Core Develop Such a System (Like Angular's @Injectable)?
This issue has been moved from a ticket on Developer Community.
Dear .NET Team,
As a full-stack developer actively working with both Angular and ASP.NET Core, I’d like to propose an enhancement to . NET’s Dependency Injection system: attribute-based service registration — similar to Angular's @Injectable().
🧠 Motivation In Angular, decorating a class with @Injectable() makes it discoverable for DI without manual registration. This greatly reduces boilerplate and improves code maintainability, especially in large projects.
In contrast, ASP.NET Core requires explicit service registration for each class, which becomes repetitive and error-prone.
🔧 Feasibility Introduce an attribute like:
[Injectable(ServiceLifetime.Scoped)]
public class MyService : IMyService { }
And support auto-registration via:
builder. Services.AutoRegisterFromAssembly();
This would:
- Simplify DI configuration
- Improve scalability for large applications
- Align with modern frameworks like Angular
- Encourage better structure and convention over configuration
🔧 Feasibility
- .NET already supports:
- Custom attributes
- Assembly scanning
- Reflection
- Source generators (for performance)
So this could be integrated cleanly into the existing DI pipeline. Thank you for considering this idea. It could make .NET Core development more streamlined and consistent with modern full-stack practices.
Sincerely, Asad Iqbal .NET & Angular Fullstack Developer
Original Comments
Feedback Bot on 7/1/2025, 00:34 AM:
Thank you for taking the time to provide your suggestion. We will do some preliminary checks to make sure we can proceed further. You will hear from us in about a week on our next steps.
I'm a bot. Here is a possible related and/or duplicate issue (I may be wrong):
- https://github.com/dotnet/extensions/issues/680
A lightweight, attribute-driven dependency injection helper for .NET — inspired by Angular’s @Injectable() decorator.
This approach lets you declare and auto-register services by simply tagging them with an [Injectable] attribute, eliminating repetitive registration boilerplate in Program.cs.
✨ Features
✅ Attribute-driven DI registration ✅ Supports all standard lifetimes (Transient, Scoped, Singleton) ✅ Automatic assembly scanning ✅ Works with or without interfaces ✅ Optional source generator support for compile-time optimization
[Injectable(ServiceLifetime.Singleton)]
public class MyService : IMyService
{
public void DoSomething() => Console.WriteLine("Hello from MyService!");
}
No need to manually register this in your startup code — it’s picked up automatically.
⚙️ Implementation
- Define the [Injectable] Attribute
using Microsoft.Extensions.DependencyInjection;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public sealed class InjectableAttribute : Attribute
{
public ServiceLifetime Lifetime { get; }
public InjectableAttribute(ServiceLifetime lifetime = ServiceLifetime.Scoped)
{
Lifetime = lifetime;
}
}
- Create an Auto-Registration Extension
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AutoRegisterFromAssembly(
this IServiceCollection services,
Assembly assembly)
{
var typesWithAttribute = assembly.GetTypes()
.Where(t => t.GetCustomAttribute<InjectableAttribute>() != null &&
t.IsClass && !t.IsAbstract);
foreach (var type in typesWithAttribute)
{
var attr = type.GetCustomAttribute<InjectableAttribute>()!;
var interfaces = type.GetInterfaces();
if (interfaces.Length == 1)
{
services.Add(new ServiceDescriptor(interfaces[0], type, attr.Lifetime));
}
else if (interfaces.Length > 1)
{
// Multiple interfaces: you can extend this logic
services.Add(new ServiceDescriptor(type, type, attr.Lifetime));
}
else
{
services.Add(new ServiceDescriptor(type, type, attr.Lifetime));
}
}
return services;
}
}
- Register Automatically in Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AutoRegisterFromAssembly(typeof(Program).Assembly);
var app = builder.Build();
app.MapGet("/", (IMyService svc) =>
{
svc.DoSomething();
return "Service invoked successfully!";
});
app.Run();
⚡ Optional: Compile-Time Optimization via Source Generator Reflection scanning happens only once at startup, but you can make it instant with a source generator that precomputes the ServiceDescriptor registrations during build time.
🧠 Libraries like Scrutor already use this concept — you can even combine [Injectable] with Scrutor’s fluent API for extra flexibility.
Example using Scrutor:
services.Scan(scan => scan
.FromAssemblyOf<Program>()
.AddClasses(classes => classes.WithAttribute<InjectableAttribute>())
.AsImplementedInterfaces()
.WithScopedLifetime());
📄 License MIT License — free to use, modify, and extend.