.NET Core ServiceProvider — ActivatorUtilities — ObjectFactory — Digitteck
.NET Core ServiceProvider — ActivatorUtilities — ObjectFactory
dotnet·14 June 2019·4 min read

.NET Core ServiceProvider — ActivatorUtilities — ObjectFactory

Dependency injection decouples modules by supplying their dependencies from the outside rather than having each class create its own. .NET Core ships a built-in DI container that handles service registration, lifetime management, and resolution — and provides two under-used helpers for constructing non-service objects.

The Problem: Tight Coupling

When classes directly instantiate their own dependencies, every change to a dependency propagates up the call chain:

csharp
// Tight coupling — each class directly depends on concrete types
public class ClassOne { }

public class ClassTwo
{
    private readonly ClassOne _classOne;
    public ClassTwo(ClassOne classOne) => _classOne = classOne;
}

public class ClassThree
{
    private readonly ClassOne _classOne;
    private readonly ClassTwo _classTwo;
    public ClassThree(ClassOne classOne, ClassTwo classTwo)
    {
        _classOne = classOne;
        _classTwo = classTwo;
    }
}

A. Creating the Service Provider

Register services in ServiceCollection (from the Microsoft.Extensions.DependencyInjection NuGet package), then build a ServiceProvider. The provider resolves all transitive dependencies automatically:

csharp
// Services registered with the DI container
public class EmpoyeesService : IEmployeesService
{
    private readonly List<Employee> _employees = new List<Employee>
    {
        new Employee("John", EmployeeGrade.I),
        new Employee("Jack", EmployeeGrade.II)
    };

    public List<Employee> FindEmployees(Expression<Func<Employee, bool>> where)
        => _employees.Where(where.Compile()).ToList();
}

public class CompensationService : ICompensationService
{
    public double GetCompensationFor(EmployeeGrade grade) => grade switch
    {
        EmployeeGrade.I   => 100,
        EmployeeGrade.II  => 1000,
        EmployeeGrade.III => double.MaxValue,
        _                 => 0
    };
}

public class PaymentsService : IPaymentsService
{
    private readonly IEmployeesService _employeesSvc;
    private readonly ICompensationService _compensationService;

    public PaymentsService(IEmployeesService empoyeeRepository, ICompensationService compensationService)
    {
        _employeesSvc        = empoyeeRepository;
        _compensationService = compensationService;
    }

    public double CalculatePaymentsForEmployeesWithFirstGrade()
    {
        var employees    = _employeesSvc.FindEmployees(x => x.Grade == EmployeeGrade.I);
        var compensation = _compensationService.GetCompensationFor(EmployeeGrade.I);
        return employees.Count * compensation;
    }
}
csharp
// Building the ServiceProvider and resolving a service
var collection = new ServiceCollection();
collection.AddScoped<IEmployeesService, EmpoyeesService>();
collection.AddScoped<ICompensationService, CompensationService>();
collection.AddScoped<IPaymentsService, PaymentsService>();

ServiceProvider serviceProvider = collection.BuildServiceProvider();

IPaymentsService paymentsSvc = serviceProvider.GetService<IPaymentsService>();
Console.WriteLine(
quot;Financial effort: {paymentsSvc.CalculatePaymentsForEmployeesWithFirstGrade()}"
);

B. ActivatorUtilities

ActivatorUtilities instantiates objects that are not registered in the container. It resolves their registered-service constructor parameters from the ServiceProvider while accepting additional runtime arguments inline. ASP.NET Core uses this internally to resolve controllers:

csharp
// ActivatorUtilities — instantiate objects NOT registered in DI
// that depend on a mix of registered services and runtime arguments

public class TheBoss
{
    // Constructor only requires registered services — ActivatorUtilities.CreateInstance resolves them
    public TheBoss(IPaymentsService paymentsService) { /* ... */ }
}

public class HREmployee
{
    // Mix of runtime arg (string) + registered service
    public HREmployee(string employeeName, IEmployeesService employeesService) { /* ... */ }
}

// Instantiate TheBoss — all constructor args come from the service provider
TheBoss boss = ActivatorUtilities.CreateInstance<TheBoss>(serviceProvider);

// Instantiate HREmployee — provide the runtime arg explicitly; service is resolved automatically
HREmployee natashaFromHr = ActivatorUtilities.CreateInstance<HREmployee>(serviceProvider, "Natasha");

C. ObjectFactory

ActivatorUtilities.CreateFactory compiles a reusable delegate. If you need to create the same type many times, compile the factory once and invoke it repeatedly — avoiding repeated reflection overhead:

csharp
// ObjectFactory — compile a delegate for repeated instantiation of the same type
// Avoids repeated reflection overhead when creating many instances

// Declare which arg types will be passed at invocation time (the rest come from DI)
ObjectFactory hrFactory = ActivatorUtilities.CreateFactory(typeof(HREmployee), new[] { typeof(string) });

// Invoke the factory — pass runtime args; DI fills the rest
HREmployee alice = hrFactory.Invoke(serviceProvider, new object[] { "Alice" }) as HREmployee;
HREmployee bob   = hrFactory.Invoke(serviceProvider, new object[] { "Bob" })   as HREmployee;

Tags

.NETC#Dependency Injection
digitteck

© 2026 Digitteck