# How the ServiceDescription Attribute Powers Dependency Injection in AntSK

> Discover how the ServiceDescription attribute in AntSK simplifies dependency injection. Learn to self-declare service interfaces and lifetimes for cleaner code and less boilerplate.

- Repository: [AIDotNet/antsk](https://github.com/aidotnet/antsk)
- Tags: internals
- Published: 2026-02-24

---

**The `ServiceDescription` attribute in AntSK enables convention-based dependency injection by allowing service implementations to self-declare their interface contracts and lifetimes, eliminating manual registration boilerplate in [`Program.cs`](https://github.com/aidotnet/antsk/blob/main/Program.cs).**

AntSK is an open-source knowledge management platform that leverages a custom dependency injection system to keep its architecture modular and maintainable. At the heart of this system lies the `ServiceDescription` attribute, a metadata marker that transforms ordinary classes into self-registering services. By annotating implementations with this attribute, developers can automate service registration while maintaining explicit control over object lifetimes.

## What Is the ServiceDescription Attribute?

The `ServiceDescription` attribute is a custom C# attribute defined in [`src/AntSK.Domain/Common/DependencyInjection/ServiceDescriptionAttribute.cs`](https://github.com/aidotnet/antsk/blob/main/src/AntSK.Domain/Common/DependencyInjection/ServiceDescriptionAttribute.cs). It acts as a declarative marker that attaches metadata to service implementation classes, telling AntSK's DI container how to register and manage instances of those classes.

### Core Metadata: ServiceType and Lifetime

The attribute captures two critical pieces of information:

- **ServiceType**: The interface or base type that the implementation fulfills (the contract)
- **Lifetime**: The object lifecycle—`Scoped`, `Singleton`, or `Transient`

```csharp
// src/AntSK.Domain/Common/DependencyInjection/ServiceDescriptionAttribute.cs
public class ServiceDescriptionAttribute : Attribute
{
    public ServiceDescriptionAttribute(Type serviceType, ServiceLifetime lifetime)
    {
        ServiceType = serviceType;
        Lifetime   = lifetime;
    }

    public Type ServiceType { get; set; }
    public ServiceLifetime Lifetime { get; set; }
}

```

## How ServiceDescription Enables Convention-Based DI

Rather than requiring explicit registration calls in [`Program.cs`](https://github.com/aidotnet/antsk/blob/main/Program.cs) for every service, AntSK uses assembly scanning to discover and register attributed classes automatically. This convention-based approach reduces boilerplate while keeping service configuration visible at the implementation site.

### The Attribute Definition

The attribute itself is lightweight, inheriting from `System.Attribute` and storing only the two properties needed for registration. When applied to a concrete class, it transforms that class into a self-describing service that carries its own registration instructions.

### Assembly Scanning and Registration

The extension method `AddServicesFromAssemblies` in [`src/AntSK.Domain/Common/DependencyInjection/DependencyInjection.cs`](https://github.com/aidotnet/antsk/blob/main/src/AntSK.Domain/Common/DependencyInjection/DependencyInjection.cs) performs the actual registration. It loads specified assemblies, iterates through types, and registers any concrete class marked with `ServiceDescriptionAttribute` using the lifetime specified in the attribute.

```csharp
// src/AntSK.Domain/Common/DependencyInjection/DependencyInjection.cs
public static IServiceCollection AddServicesFromAssemblies(this IServiceCollection services, params string[] assemblies)
{
    Type attributeType = typeof(ServiceDescriptionAttribute);
    foreach (var item in assemblies)
    {
        Assembly assembly = Assembly.Load(item);
        foreach (var classType in assembly.GetTypes())
        {
            if (!classType.IsAbstract && classType.IsClass && classType.IsDefined(attributeType, false))
            {
                var serviceAttribute = classType.GetCustomAttribute(attributeType) as ServiceDescriptionAttribute;
                switch (serviceAttribute!.Lifetime)
                {
                    case ServiceLifetime.Scoped:
                        services.AddScoped(serviceAttribute.ServiceType, classType); break;
                    case ServiceLifetime.Singleton:
                        services.AddSingleton(serviceAttribute.ServiceType, classType); break;
                    case ServiceLifetime.Transient:
                        services.AddTransient(serviceAttribute.ServiceType, classType); break;
                }
            }
        }
    }
    return services;
}

```

## Practical Usage Examples

Implementing services with the `ServiceDescription` attribute requires minimal ceremony. Developers annotate the implementation class with the attribute, specify the interface and lifetime, and the framework handles registration automatically.

### Declaring Services with ServiceDescription

To register a service, apply the attribute to the concrete implementation class, passing the contract type and desired lifetime:

```csharp
// src/AntSK/Services/Template/UserService.cs
[ServiceDescription(typeof(IUserService), ServiceLifetime.Scoped)]
public class UserService : IUserService 
{ 
    // Implementation details...
}

```

This declaration registers `UserService` as the implementation for `IUserService` with a **scoped** lifetime, meaning a new instance is created for each HTTP request.

Other lifetime configurations follow the same pattern:

```csharp
// Declaring a singleton service
[ServiceDescription(typeof(IApiService), ServiceLifetime.Singleton)]
public class ApiService : IApiService
{
    // Single instance shared across the application
}

// Declaring a transient service
[ServiceDescription(typeof(IEmailSender), ServiceLifetime.Transient)]
public class EmailSender : IEmailSender
{
    // New instance created every time the service is requested
}

```

### Configuring the Host

The host application only needs to specify which assemblies to scan. In [`Program.cs`](https://github.com/aidotnet/antsk/blob/main/Program.cs) or the application host setup, call `AddServicesFromAssemblies` with the assembly names containing your services:

```csharp
// Startup (Program.cs) – only load assemblies, no per-service calls
var builder = DistributedApplication.CreateBuilder(args);
builder.Services.AddServicesFromAssemblies(
    "AntSK",               // main project
    "AntSK.Domain",        // domain layer
    "AntSK.Services");    // service layer
builder.Build().Run();

```

This approach keeps the host configuration minimal and focused on composition rather than registration details.

## Summary

The `ServiceDescription` attribute transforms AntSK's dependency injection from an explicit registration model into a convention-driven system:

- **Self-documenting services** carry their own contract and lifetime metadata via the `ServiceDescription` attribute defined in [`ServiceDescriptionAttribute.cs`](https://github.com/aidotnet/antsk/blob/main/ServiceDescriptionAttribute.cs)
- **Automatic discovery** through `AddServicesFromAssemblies` eliminates boilerplate registration code in [`Program.cs`](https://github.com/aidotnet/antsk/blob/main/Program.cs)
- **Explicit lifetime control** allows developers to specify `Scoped`, `Singleton`, or `Transient` behavior at the implementation level
- **Modular architecture** supports loading services from multiple assemblies without coupling the host to specific implementation details

## Frequently Asked Questions

### What is the ServiceDescription attribute in AntSK?

The `ServiceDescription` attribute is a custom metadata marker defined in [`src/AntSK.Domain/Common/DependencyInjection/ServiceDescriptionAttribute.cs`](https://github.com/aidotnet/antsk/blob/main/src/AntSK.Domain/Common/DependencyInjection/ServiceDescriptionAttribute.cs) that attaches dependency injection configuration directly to service implementation classes. It stores the service contract type (`ServiceType`) and the object lifetime (`ServiceLifetime`), allowing the framework to automatically register the class with the DI container without explicit `services.Add...` calls in the host configuration.

### How does ServiceDescription differ from standard ASP.NET Core DI registration?

Standard ASP.NET Core DI requires explicit registration in [`Program.cs`](https://github.com/aidotnet/antsk/blob/main/Program.cs) or [`Startup.cs`](https://github.com/aidotnet/antsk/blob/main/Startup.cs) using methods like `services.AddScoped<IMyService, MyService>()`. In contrast, AntSK's `ServiceDescription` attribute enables **convention-based registration** where the service class declares its own contract and lifetime via the attribute, and the `AddServicesFromAssemblies` extension method scans assemblies to perform registration automatically. This reduces boilerplate and keeps service configuration co-located with the implementation.

### Which service lifetimes does ServiceDescription support?

The `ServiceDescription` attribute supports all three standard ASP.NET Core service lifetimes via the `ServiceLifetime` enum: **Scoped** (new instance per request), **Singleton** (single instance shared across the application), and **Transient** (new instance every time the service is resolved). These are specified as the second parameter to the attribute constructor, such as `[ServiceDescription(typeof(IMyService), ServiceLifetime.Scoped)]`.

### How does AntSK scan for services across multiple assemblies?

AntSK uses the `AddServicesFromAssemblies` extension method defined in [`src/AntSK.Domain/Common/DependencyInjection/DependencyInjection.cs`](https://github.com/aidotnet/antsk/blob/main/src/AntSK.Domain/Common/DependencyInjection/DependencyInjection.cs) to scan for services. This method accepts assembly names as string parameters, loads each assembly using `Assembly.Load()`, iterates through all types, and identifies concrete classes marked with the `ServiceDescription` attribute. It then registers each discovered service with the `IServiceCollection` using the lifetime specified in the attribute, enabling modular composition where different layers (Domain, Services) can be loaded without the host knowing specific implementation details.