Exploring ServiceDescriptor in .NET

Exploring ServiceDescriptor in .NET

Introduction

In the previous post we had seen the IServiceCollection interface and the SeviceCollection class. You can refer to my previous post here Exploring ServiceCollection in .NET

IServiceCollection interface extends the generic IList with the generic type as ServiceDescriptor. In this post lets us explore the ServiceDescriptor in detail and see how it is used by the Microsoft.Extensions.DependencyInjection DI framework.

Please note that this post has lots of code from microsoft repository. It is mentioned for reference purpose and it is fine if you are not able to understand it completely and feel free to skip it. The main idea is to understand the role of ServiceDescriptor in the registration of the service in the ServiceCollection.

What is a ServiceDescriptor ?

ServiceDescriptor is a class that represents the service that is injected into the ServiceCollection which is our dependency injection container. This class contains multiple overloads of the contructor to support flexibility in how the objects are created. It contains various properties and multiple overloaded methods for registering, resolving and instantiating the service injected in the container.

Constructors in ServiceDescriptor

The ServiceDescriptor class contains both the public and the private contructor in order to manage how the different instances of the service is created. The public constructors are chained to call the other overloaded version of the constructor and finally calling the private contructor and setting the ServiceKey, ServiceType, Lifetime.

We can call the ServiceDescriptor separately to construct our Services registration manually or we can call the ServiceCollectionServiceExtension methods namely AddScoped, AddTransient, AddSingleton to register our services. These extension methods internally calls the ServiceDescriptor contructor to represent our services which later builds the ServiceProvider that actually creates instances of the service as per the sepecification.

The public and private constructors of the ServiceDescriptor class is listed below. All these constructors are doing is setting the ServiceType,ServiceKey and Lifetime for the registered service. Additionally it also sets any one of the ImplementationType, ImplementationInstance or ImplementationFactory for service representation.

Public Constructors

/// <summary>
/// Initializes a new instance of <see cref="ServiceDescriptor"/> with the specified <paramref name="implementationType"/>.
/// </summary>
/// <param name="serviceType">The <see cref="Type"/> of the service.</param>
/// <param name="implementationType">The <see cref="Type"/> implementing the service.</param>
/// <param name="lifetime">The <see cref="ServiceLifetime"/> of the service.</param>
public ServiceDescriptor(
    Type serviceType,
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType,
    ServiceLifetime lifetime)
    : this(serviceType, null, implementationType, lifetime)
{
}

/// <summary>
/// Initializes a new instance of <see cref="ServiceDescriptor"/> with the specified <paramref name="implementationType"/>.
/// </summary>
/// <param name="serviceType">The <see cref="Type"/> of the service.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationType">The <see cref="Type"/> implementing the service.</param>
/// <param name="lifetime">The <see cref="ServiceLifetime"/> of the service.</param>
public ServiceDescriptor(
    Type serviceType,
    object? serviceKey,
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType,
    ServiceLifetime lifetime)
    : this(serviceType, serviceKey, lifetime)
{
    ThrowHelper.ThrowIfNull(serviceType);
    ThrowHelper.ThrowIfNull(implementationType);

    _implementationType = implementationType;
}

/// <summary>
/// Initializes a new instance of <see cref="ServiceDescriptor"/> with the specified <paramref name="instance"/>
/// as a <see cref="ServiceLifetime.Singleton"/>.
/// </summary>
/// <param name="serviceType">The <see cref="Type"/> of the service.</param>
/// <param name="instance">The instance implementing the service.</param>
public ServiceDescriptor(
    Type serviceType,
    object instance)
    : this(serviceType, null, instance)
{
}

/// <summary>
/// Initializes a new instance of <see cref="ServiceDescriptor"/> with the specified <paramref name="instance"/>
/// as a <see cref="ServiceLifetime.Singleton"/>.
/// </summary>
/// <param name="serviceType">The <see cref="Type"/> of the service.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="instance">The instance implementing the service.</param>
public ServiceDescriptor(
    Type serviceType,
    object? serviceKey,
    object instance)
    : this(serviceType, serviceKey, ServiceLifetime.Singleton)
{
    ThrowHelper.ThrowIfNull(serviceType);
    ThrowHelper.ThrowIfNull(instance);

    _implementationInstance = instance;
}

/// <summary>
/// Initializes a new instance of <see cref="ServiceDescriptor"/> with the specified <paramref name="factory"/>.
/// </summary>
/// <param name="serviceType">The <see cref="Type"/> of the service.</param>
/// <param name="factory">A factory used for creating service instances.</param>
/// <param name="lifetime">The <see cref="ServiceLifetime"/> of the service.</param>
public ServiceDescriptor(
    Type serviceType,
    Func<IServiceProvider, object> factory,
    ServiceLifetime lifetime)
    : this(serviceType, serviceKey: null, lifetime)
{
    ThrowHelper.ThrowIfNull(serviceType);
    ThrowHelper.ThrowIfNull(factory);

    _implementationFactory = factory;
}

/// <summary>
/// Initializes a new instance of <see cref="ServiceDescriptor"/> with the specified <paramref name="factory"/>.
/// </summary>
/// <param name="serviceType">The <see cref="Type"/> of the service.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="factory">A factory used for creating service instances.</param>
/// <param name="lifetime">The <see cref="ServiceLifetime"/> of the service.</param>
public ServiceDescriptor(
    Type serviceType,
    object? serviceKey,
    Func<IServiceProvider, object?, object> factory,
    ServiceLifetime lifetime)
    : this(serviceType, serviceKey, lifetime)
{
    ThrowHelper.ThrowIfNull(serviceType);
    ThrowHelper.ThrowIfNull(factory);

    if (serviceKey is null)
    {
        // If the key is null, use the same factory signature as non-keyed descriptor
        Func<IServiceProvider, object> nullKeyedFactory = sp => factory(sp, null);
        _implementationFactory = nullKeyedFactory;
    }
    else
    {
        _implementationFactory = factory;
    }
}

Private Constructor

private ServiceDescriptor(Type serviceType, object? serviceKey, ServiceLifetime lifetime)
{
    Lifetime = lifetime;
    ServiceType = serviceType;
    ServiceKey = serviceKey;
}

It might also be possible that multiple implementations of the same type is injected in the container , in such scenarios in order to differentiate the service implementation we need to associate the particular service with a key while registering.

The ServiceDescriptor class thus has set of methods with and without the key based implementation. The key based implementation is named with they KeyedTransient, KeyedScoped etc...

Properties in ServiceDescriptor

/// <summary>
/// Gets the <see cref="ServiceLifetime"/> of the service.
/// </summary>
public ServiceLifetime Lifetime { get; }

/// <summary>
/// Get the key of the service, if applicable.
/// </summary>
public object? ServiceKey { get; }

/// <summary>
/// Gets the <see cref="Type"/> of the service.
/// </summary>
public Type ServiceType { get; }

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
private Type? _implementationType;

/// <summary>
/// Gets the <see cref="Type"/> that implements the service.
/// </summary>
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
public Type? ImplementationType
{
    get
    {
        if (IsKeyedService)
        {
            ThrowKeyedDescriptor();
        }
        return _implementationType;
    }
}

/// <summary>
/// Gets the <see cref="Type"/> that implements the service.
/// </summary>
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
public Type? KeyedImplementationType
{
    get
    {
        if (!IsKeyedService)
        {
            ThrowNonKeyedDescriptor();
        }
        return _implementationType;
    }
}

private object? _implementationInstance;

/// <summary>
/// Gets the instance that implements the service.
/// </summary>
public object? ImplementationInstance
{
    get
    {
        if (IsKeyedService)
        {
            ThrowKeyedDescriptor();
        }
        return _implementationInstance;
    }
}

/// <summary>
/// Gets the instance that implements the service.
/// </summary>
public object? KeyedImplementationInstance
{
    get
    {
        if (!IsKeyedService)
        {
            ThrowNonKeyedDescriptor();
        }
        return _implementationInstance;
    }
}

private object? _implementationFactory;

/// <summary>
/// Gets the factory used for creating service instances.
/// </summary>
public Func<IServiceProvider, object>? ImplementationFactory
{
    get
    {
        if (IsKeyedService)
        {
            ThrowKeyedDescriptor();
        }
        return (Func<IServiceProvider, object>?) _implementationFactory;
    }
}

/// <summary>
/// Gets the factory used for creating Keyed service instances.
/// </summary>
public Func<IServiceProvider, object?, object>? KeyedImplementationFactory
{
    get
    {
        if (!IsKeyedService)
        {
            ThrowNonKeyedDescriptor();
        }
        return (Func<IServiceProvider, object?, object>?) _implementationFactory;
    }
}

/// <summary>
/// Indicates whether the service is a keyed service.
/// </summary>
public bool IsKeyedService => ServiceKey != null;

For any ServiceDescriptor the properties ServiceType and Lifetime would be set by the constructor. They will also have any one of the following properties -

  • ImplementationType (Type?) representing the type which will be resolved whenever an instance of the serviceType is required.
  • ImplementationInstance (object?) which is an actual instance of the serviceType which already exists.
  • ImplementationFactory (Func<IServiceProvider, object>) which is a Func that given an IServiceProvider can return an object which can be implicitly converted to the serviceType.

Methods in ServiceDescriptor

The ServiceDescriptor class contains static methods that are used to create new instances of the ServiceDescriptor based on the service type and lifetime. The list of methods is huge but the idea is the same. All of these methods provides an instance of ServiceDescriptor that represents the service to be registered.

These methods are called in the ServiceCollectionDescriptorExtension methods like TryAddTransient, TryAddScoped etc...

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// and the <see cref="ServiceLifetime.Transient"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <typeparam name="TImplementation">The type of the implementation.</typeparam>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Transient<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>()
    where TService : class
    where TImplementation : class, TService
{
    return DescribeKeyed<TService, TImplementation>(null, ServiceLifetime.Transient);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// and the <see cref="ServiceLifetime.Transient"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <typeparam name="TImplementation">The type of the implementation.</typeparam>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor KeyedTransient<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(object? serviceKey)
    where TService : class
    where TImplementation : class, TService
{
    return DescribeKeyed<TService, TImplementation>(serviceKey, ServiceLifetime.Transient);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="service"/> and <paramref name="implementationType"/>
/// and the <see cref="ServiceLifetime.Transient"/> lifetime.
/// </summary>
/// <param name="service">The type of the service.</param>
/// <param name="implementationType">The type of the implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Transient(
    Type service,
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType)
{
    ThrowHelper.ThrowIfNull(service);
    ThrowHelper.ThrowIfNull(implementationType);

    return Describe(service, implementationType, ServiceLifetime.Transient);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="service"/> and <paramref name="implementationType"/>
/// and the <see cref="ServiceLifetime.Transient"/> lifetime.
/// </summary>
/// <param name="service">The type of the service.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationType">The type of the implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor KeyedTransient(
    Type service,
    object? serviceKey,
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType)
{
    ThrowHelper.ThrowIfNull(service);
    ThrowHelper.ThrowIfNull(implementationType);

    return DescribeKeyed(service, serviceKey, implementationType, ServiceLifetime.Transient);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Transient"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <typeparam name="TImplementation">The type of the implementation.</typeparam>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Transient<TService, TImplementation>(
    Func<IServiceProvider, TImplementation> implementationFactory)
    where TService : class
    where TImplementation : class, TService
{
    ThrowHelper.ThrowIfNull(implementationFactory);

    return Describe(typeof(TService), implementationFactory, ServiceLifetime.Transient);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Transient"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <typeparam name="TImplementation">The type of the implementation.</typeparam>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor KeyedTransient<TService, TImplementation>(
    object? serviceKey,
    Func<IServiceProvider, object?, TImplementation> implementationFactory)
    where TService : class
    where TImplementation : class, TService
{
    ThrowHelper.ThrowIfNull(implementationFactory);

    return DescribeKeyed(typeof(TService), serviceKey, implementationFactory, ServiceLifetime.Transient);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Transient"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Transient<TService>(Func<IServiceProvider, TService> implementationFactory)
    where TService : class
{
    ThrowHelper.ThrowIfNull(implementationFactory);

    return Describe(typeof(TService), implementationFactory, ServiceLifetime.Transient);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Transient"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor KeyedTransient<TService>(object? serviceKey, Func<IServiceProvider, object?, TService> implementationFactory)
    where TService : class
{
    ThrowHelper.ThrowIfNull(implementationFactory);

    return DescribeKeyed(typeof(TService), serviceKey, implementationFactory, ServiceLifetime.Transient);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="service"/>, <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Transient"/> lifetime.
/// </summary>
/// <param name="service">The type of the service.</param>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Transient(Type service, Func<IServiceProvider, object> implementationFactory)
{
    ThrowHelper.ThrowIfNull(service);
    ThrowHelper.ThrowIfNull(implementationFactory);

    return Describe(service, implementationFactory, ServiceLifetime.Transient);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="service"/>, <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Transient"/> lifetime.
/// </summary>
/// <param name="service">The type of the service.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor KeyedTransient(Type service, object? serviceKey, Func<IServiceProvider, object?, object> implementationFactory)
{
    ThrowHelper.ThrowIfNull(service);
    ThrowHelper.ThrowIfNull(implementationFactory);

    return DescribeKeyed(service, serviceKey, implementationFactory, ServiceLifetime.Transient);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <typeparam name="TImplementation">The type of the implementation.</typeparam>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Scoped<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>()
    where TService : class
    where TImplementation : class, TService
{
    return DescribeKeyed<TService, TImplementation>(null, ServiceLifetime.Scoped);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <typeparam name="TImplementation">The type of the implementation.</typeparam>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor KeyedScoped<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(object? serviceKey)
    where TService : class
    where TImplementation : class, TService
{
    return DescribeKeyed<TService, TImplementation>(serviceKey, ServiceLifetime.Scoped);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="service"/> and <paramref name="implementationType"/>
/// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <param name="service">The type of the service.</param>
/// <param name="implementationType">The type of the implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Scoped(
    Type service,
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType)
{
    return Describe(service, implementationType, ServiceLifetime.Scoped);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="service"/> and <paramref name="implementationType"/>
/// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <param name="service">The type of the service.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationType">The type of the implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor KeyedScoped(
    Type service,
    object? serviceKey,
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType)
{
    return DescribeKeyed(service, serviceKey, implementationType, ServiceLifetime.Scoped);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <typeparam name="TImplementation">The type of the implementation.</typeparam>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Scoped<TService, TImplementation>(
    Func<IServiceProvider, TImplementation> implementationFactory)
    where TService : class
    where TImplementation : class, TService
{
    ThrowHelper.ThrowIfNull(implementationFactory);

    return Describe(typeof(TService), implementationFactory, ServiceLifetime.Scoped);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <typeparam name="TImplementation">The type of the implementation.</typeparam>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor KeyedScoped<TService, TImplementation>(
    object? serviceKey,
    Func<IServiceProvider, object?, TImplementation> implementationFactory)
    where TService : class
    where TImplementation : class, TService
{
    ThrowHelper.ThrowIfNull(implementationFactory);

    return DescribeKeyed(typeof(TService), serviceKey, implementationFactory, ServiceLifetime.Scoped);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Scoped<TService>(Func<IServiceProvider, TService> implementationFactory)
    where TService : class
{
    ThrowHelper.ThrowIfNull(implementationFactory);

    return Describe(typeof(TService), implementationFactory, ServiceLifetime.Scoped);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor KeyedScoped<TService>(object? serviceKey, Func<IServiceProvider, object?, TService> implementationFactory)
    where TService : class
{
    ThrowHelper.ThrowIfNull(implementationFactory);

    return DescribeKeyed(typeof(TService), serviceKey, implementationFactory, ServiceLifetime.Scoped);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="service"/>, <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <param name="service">The type of the service.</param>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Scoped(Type service, Func<IServiceProvider, object> implementationFactory)
{
    ThrowHelper.ThrowIfNull(service);
    ThrowHelper.ThrowIfNull(implementationFactory);

    return Describe(service, implementationFactory, ServiceLifetime.Scoped);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="service"/>, <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <param name="service">The type of the service.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor KeyedScoped(Type service, object? serviceKey, Func<IServiceProvider, object?, object> implementationFactory)
{
    ThrowHelper.ThrowIfNull(service);
    ThrowHelper.ThrowIfNull(implementationFactory);

    return DescribeKeyed(service, serviceKey, implementationFactory, ServiceLifetime.Scoped);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <typeparam name="TImplementation">The type of the implementation.</typeparam>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Singleton<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>()
    where TService : class
    where TImplementation : class, TService
{
    return DescribeKeyed<TService, TImplementation>(null, ServiceLifetime.Singleton);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <typeparam name="TImplementation">The type of the implementation.</typeparam>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor KeyedSingleton<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(
    object? serviceKey)
    where TService : class
    where TImplementation : class, TService
{
    return DescribeKeyed<TService, TImplementation>(serviceKey, ServiceLifetime.Singleton);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="service"/> and <paramref name="implementationType"/>
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <param name="service">The type of the service.</param>
/// <param name="implementationType">The type of the implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Singleton(
    Type service,
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType)
{
    ThrowHelper.ThrowIfNull(service);
    ThrowHelper.ThrowIfNull(implementationType);

    return Describe(service, implementationType, ServiceLifetime.Singleton);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="service"/> and <paramref name="implementationType"/>
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <param name="service">The type of the service.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationType">The type of the implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor KeyedSingleton(
    Type service,
    object? serviceKey,
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType)
{
    ThrowHelper.ThrowIfNull(service);
    ThrowHelper.ThrowIfNull(implementationType);

    return DescribeKeyed(service, serviceKey, implementationType, ServiceLifetime.Singleton);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <typeparam name="TImplementation">The type of the implementation.</typeparam>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Singleton<TService, TImplementation>(
    Func<IServiceProvider, TImplementation> implementationFactory)
    where TService : class
    where TImplementation : class, TService
{
    ThrowHelper.ThrowIfNull(implementationFactory);

    return Describe(typeof(TService), implementationFactory, ServiceLifetime.Singleton);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <typeparam name="TImplementation">The type of the implementation.</typeparam>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor KeyedSingleton<TService, TImplementation>(
    object? serviceKey,
    Func<IServiceProvider, object?, TImplementation> implementationFactory)
    where TService : class
    where TImplementation : class, TService
{
    ThrowHelper.ThrowIfNull(implementationFactory);

    return DescribeKeyed(typeof(TService), serviceKey, implementationFactory, ServiceLifetime.Singleton);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Singleton<TService>(Func<IServiceProvider, TService> implementationFactory)
    where TService : class
{
    ThrowHelper.ThrowIfNull(implementationFactory);

    return Describe(typeof(TService), implementationFactory, ServiceLifetime.Singleton);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor KeyedSingleton<TService>(
    object? serviceKey,
    Func<IServiceProvider, object?, TService> implementationFactory)
    where TService : class
{
    ThrowHelper.ThrowIfNull(implementationFactory);

    return DescribeKeyed(typeof(TService), serviceKey, implementationFactory, ServiceLifetime.Singleton);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="serviceType"/>, <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <param name="serviceType">The type of the service.</param>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Singleton(
    Type serviceType,
    Func<IServiceProvider, object> implementationFactory)
{
    ThrowHelper.ThrowIfNull(serviceType);
    ThrowHelper.ThrowIfNull(implementationFactory);

    return Describe(serviceType, implementationFactory, ServiceLifetime.Singleton);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="serviceType"/>, <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <param name="serviceType">The type of the service.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor KeyedSingleton(
    Type serviceType,
    object? serviceKey,
    Func<IServiceProvider, object?, object> implementationFactory)
{
    ThrowHelper.ThrowIfNull(serviceType);
    ThrowHelper.ThrowIfNull(implementationFactory);

    return DescribeKeyed(serviceType, serviceKey, implementationFactory, ServiceLifetime.Singleton);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <paramref name="implementationInstance"/>,
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <param name="implementationInstance">The instance of the implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Singleton<TService>(TService implementationInstance)
    where TService : class
{
    ThrowHelper.ThrowIfNull(implementationInstance);

    return Singleton(serviceType: typeof(TService), implementationInstance: implementationInstance);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <paramref name="implementationInstance"/>,
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationInstance">The instance of the implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor KeyedSingleton<TService>(
    object? serviceKey,
    TService implementationInstance)
    where TService : class
{
    ThrowHelper.ThrowIfNull(implementationInstance);

    return KeyedSingleton(typeof(TService), serviceKey, implementationInstance);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="serviceType"/>, <paramref name="implementationInstance"/>,
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <param name="serviceType">The type of the service.</param>
/// <param name="implementationInstance">The instance of the implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Singleton(
    Type serviceType,
    object implementationInstance)
{
    ThrowHelper.ThrowIfNull(serviceType);
    ThrowHelper.ThrowIfNull(implementationInstance);

    return new ServiceDescriptor(serviceType, implementationInstance);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="serviceType"/>, <paramref name="implementationInstance"/>,
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <param name="serviceType">The type of the service.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationInstance">The instance of the implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor KeyedSingleton(
    Type serviceType,
    object? serviceKey,
    object implementationInstance)
{
    ThrowHelper.ThrowIfNull(serviceType);
    ThrowHelper.ThrowIfNull(implementationInstance);

    return new ServiceDescriptor(serviceType, serviceKey, implementationInstance);
}

private static ServiceDescriptor DescribeKeyed<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(
    object? serviceKey,
    ServiceLifetime lifetime)
    where TService : class
    where TImplementation : class, TService
{
    return DescribeKeyed(
        typeof(TService),
        serviceKey,
        typeof(TImplementation),
        lifetime: lifetime);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="serviceType"/>, <paramref name="implementationType"/>,
/// and <paramref name="lifetime"/>.
/// </summary>
/// <param name="serviceType">The type of the service.</param>
/// <param name="implementationType">The type of the implementation.</param>
/// <param name="lifetime">The lifetime of the service.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Describe(
    Type serviceType,
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType,
    ServiceLifetime lifetime)
{
    return new ServiceDescriptor(serviceType, implementationType, lifetime);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="serviceType"/>, <paramref name="implementationType"/>,
/// and <paramref name="lifetime"/>.
/// </summary>
/// <param name="serviceType">The type of the service.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationType">The type of the implementation.</param>
/// <param name="lifetime">The lifetime of the service.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor DescribeKeyed(
    Type serviceType,
    object? serviceKey,
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType,
    ServiceLifetime lifetime)
{
    return new ServiceDescriptor(serviceType, serviceKey, implementationType, lifetime);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="serviceType"/>, <paramref name="implementationFactory"/>,
/// and <paramref name="lifetime"/>.
/// </summary>
/// <param name="serviceType">The type of the service.</param>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <param name="lifetime">The lifetime of the service.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Describe(Type serviceType, Func<IServiceProvider, object> implementationFactory, ServiceLifetime lifetime)
{
    return new ServiceDescriptor(serviceType, implementationFactory, lifetime);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="serviceType"/>, <paramref name="implementationFactory"/>,
/// and <paramref name="lifetime"/>.
/// </summary>
/// <param name="serviceType">The type of the service.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <param name="lifetime">The lifetime of the service.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor DescribeKeyed(Type serviceType, object? serviceKey, Func<IServiceProvider, object?, object> implementationFactory, ServiceLifetime lifetime)
{
    return new ServiceDescriptor(serviceType, serviceKey, implementationFactory, lifetime);
}
Example using Transient static method in the ServiceCollectionServiceExtension method
public static void TryAddTransient(
    this IServiceCollection collection,
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type service)
{
    ThrowHelper.ThrowIfNull(collection);
    ThrowHelper.ThrowIfNull(service);

    var descriptor = ServiceDescriptor.Transient(service, service);
    TryAdd(collection, descriptor);
}

How is the ServiceDescriptor used in Dependency Injection ?

The ServiceCollection class has various set of extension methods. These methods are present in different files separated physically in the dependency injection source code.

ServiceCollectionServiceExtensions - This file contains the extension methods like AddScoped, AddTransient and AddSingleton. These methods invoke the ServiceDescriptor contructor to create an instance to add to ServiceCollection.

public static IServiceCollection AddTransient(
    this IServiceCollection services,
    Type serviceType,
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType)
{
    ThrowHelper.ThrowIfNull(services);
    ThrowHelper.ThrowIfNull(serviceType);
    ThrowHelper.ThrowIfNull(implementationType);

    return Add(services, serviceType, implementationType, ServiceLifetime.Transient);
}
private static IServiceCollection Add(
    IServiceCollection collection,
    Type serviceType,
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType,
    ServiceLifetime lifetime)
{
    var descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime);
    collection.Add(descriptor);
    return collection;
}

ServiceCollectionDescriptorExtensions - This file contains extension methods like TryAddTransient that creates an instance of ServiceDescriptor and adds it to the ServiceCollection.

public static void TryAddTransient(
    this IServiceCollection collection,
    Type service,
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType)
{
    ThrowHelper.ThrowIfNull(collection);
    ThrowHelper.ThrowIfNull(service);
    ThrowHelper.ThrowIfNull(implementationType);

    var descriptor = ServiceDescriptor.Transient(service, implementationType);
    TryAdd(collection, descriptor);
}
public static void TryAdd(
    this IServiceCollection collection,
    ServiceDescriptor descriptor)
{
    ThrowHelper.ThrowIfNull(collection);
    ThrowHelper.ThrowIfNull(descriptor);

    int count = collection.Count;
    for (int i = 0; i < count; i++)
    {
        if (collection[i].ServiceType == descriptor.ServiceType
            && collection[i].ServiceKey == descriptor.ServiceKey)
        {
            // Already added
            return;
        }
    }

    collection.Add(descriptor);
}

Conclusion

In this post we explored the ServiceDescriptor class in detail. It has a bunch of overloaded public contructors and a private constructor that creates an instance of ServiceDescriptor that represents the service to be injected.

It also has three main properties SericeType, ServiceKey, Lifetime that plays an important role in representing the details of the service to be injected. Another property is "IsKeyedService" which denotes if the service is a KeyedService.

KeyService are the services that registers itself with a ServiceKey to differentiate itself from another Service of the same type registered in the container.

The ServiceDescriptor is used in the ServiceCollectionServiceExtension and ServiceCollectionDescriptorExtension methods to create an instance of the ServiceDescriptor so that it can be added to the ServiceCollection.

Now that we are familiar with the ServiceCollection and ServiceDescriptor we will next look into the ServiceProvider in our next post.

Thank you for reading the post and see you in the next post.

Buy a coffee for sudshekhar