Multiple Approaches for Passing Constructor Parameters in .NET Core Dependency Injection

Nov 23, 2025 · Programming · 8 views · 7.8

Keywords: .NET Core | Dependency Injection | Constructor Parameters | Factory Delegates | ActivatorUtilities

Abstract: This article provides an in-depth exploration of various methods for passing parameters to constructors within the .NET Core dependency injection container. It focuses on factory delegates and the ActivatorUtilities helper class, comparing their applicability and performance characteristics. Through practical code examples, it demonstrates proper handling of service dependencies and runtime parameters, offering comprehensive solutions for parameter injection.

The Challenge of Parameter Passing in Dependency Injection

In modern application development, dependency injection has become the standard pattern for managing object dependencies. However, parameter passing becomes complex when constructors contain both service dependencies and ordinary parameters. Consider the following service definition:

public class Service : IService
{
    public Service(IOtherService service1, IAnotherOne service2, string arg)
    {
        // Constructor implementation
    }
}

In this example, the constructor requires two service dependencies and one string parameter. Traditional dependency injection registration cannot handle this scenario directly, requiring specialized techniques.

Factory Delegate Approach

The most straightforward method involves using factory delegates. In .NET Core's dependency injection container, the AddSingleton, AddTransient, and AddScoped methods all support accepting factory functions as parameters.

Basic Factory Delegate Implementation

Here is the correct implementation using factory delegates:

services.AddSingleton<IService>(x => 
    new Service(x.GetRequiredService<IOtherService>(),
                x.GetRequiredService<IAnotherOne>(), 
                "parameter value"));

The key points here are:

Incorrect Implementation Example

Avoid this erroneous implementation:

// Wrong example - do not call BuildServiceProvider() inside factory
services.AddSingleton<IService>(x =>
    new Service(
        services.BuildServiceProvider().GetService<IOtherService>(),
        services.BuildServiceProvider().GetService<IAnotherOne>(),
        ""));

This implementation causes repeated building of the service container, potentially leading to performance issues and unexpected lifecycle behaviors.

ActivatorUtilities Helper Class

For more complex scenarios, .NET Core provides the ActivatorUtilities helper class, which intelligently mixes service resolution with direct parameter injection.

CreateInstance Method

The ActivatorUtilities.CreateInstance<T> method automatically resolves service dependencies while allowing specification of parameters for direct injection:

services.AddSingleton<IService>(x => 
    ActivatorUtilities.CreateInstance<Service>(x, "parameter value"));

This method works by:

Dependency Replacement Scenario

ActivatorUtilities is particularly useful for scenarios requiring temporary replacement of specific dependencies:

var specialService = ActivatorUtilities.CreateInstance<Service>(serviceProvider,
    new OtherServiceB());

In this example, the first parameter of type IOtherService is replaced by the OtherServiceB instance, while other dependencies are still resolved from the service container.

Performance Optimization Solutions

For high-frequency creation scenarios, factory caching can significantly improve performance.

CreateFactory Method

The ActivatorUtilities.CreateFactory method creates reusable factory functions:

var serviceFactory = ActivatorUtilities.CreateFactory(typeof(Service), 
    new Type[] { typeof(string) });

// Invoke factory when needed
var serviceInstance = serviceFactory(serviceProvider, "runtime parameter");

This approach avoids reflection overhead during each instance creation, making it particularly suitable for high-request scenarios like SignalR.

Practical Application Example

Here is a complete application example demonstrating how to use these techniques in real projects:

public class DemoService
{
    private readonly HelloWorldService helloWorldService;
    private readonly string firstname;
    private readonly string lastname;

    public DemoService(HelloWorldService helloWorldService, string firstname, string lastname)
    {
        this.helloWorldService = helloWorldService;
        this.firstname = firstname;
        this.lastname = lastname;
    }

    public string HelloWorld()
    {
        return this.helloWorldService.Hello(firstname, lastname);
    }
}

// Service registration
services.AddTransient<HelloWorldService>();
services.AddTransient<DemoService>(provider =>
    ActivatorUtilities.CreateInstance<DemoService>(provider, "Tseng", "Stackoverflow"));

Method Selection Guide

When choosing parameter passing methods, consider the following factors:

Conclusion

The .NET Core dependency injection container offers multiple flexible parameter passing mechanisms. Factory delegates provide straightforward solutions for most scenarios; the ActivatorUtilities helper class offers advanced mixed injection capabilities; and factory caching provides optimization for performance-sensitive situations. Developers should select appropriate methods based on specific requirements to ensure code maintainability and performance.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.