Migrate from IContainer and reflection-based registration
Arc4u 8.x wrapped the .NET container in its own abstraction (IContainer, IContainerRegistry,
IContainerResolve, DependencyContext) and registered [Export] classes at startup by reflection
(InitializeFromConfig). Arc4u 9 removes that layer and relies on the keyed services added in
.NET 8, which replace resolution by name
(#140). This page shows how to move an application
to the source generators and keyed services. The full list of 8.x to 9 changes is in the
migration guide.
What changed
| Arc4u 8.x | Arc4u 9 |
|---|---|
Arc4u.Standard.Dependency |
Arc4u.Dependency: the attributes and a few IServiceProvider and IServiceCollection helpers. |
Arc4u.Standard.Dependency.ComponentModel (ComponentModelContainer) |
Not shipped (see Package support). Use IServiceCollection directly. |
InitializeFromConfig scans the Assemblies and RegisterTypes of the Application.Dependency section at startup, with Assembly.Load and Type.GetType. |
Arc4u.Dependency.Tool generates the registrations at build time: one Register<Name>Types method per project, and RegisterTypes for the types listed in Configs/appsettings.json. |
IContainerResolve.Resolve<T>(name) |
.NET keyed services: [FromKeyedServices(name)], GetKeyedService<T>(name). |
DependencyContext.Current (static access to the container) |
Constructor injection, or an injected IServiceProvider. |
[Export], [Shared] and [Scoped] are still in the Arc4u.Dependency.Attribute namespace, and
a class gets the same lifetime as in 8.x. As in 8.x, every [Export] of a class is registered, and
the attribute is recognized whatever its spelling ([Export], [ExportAttribute], a fully
qualified name or a using alias); see
What is registered.
Steps
Replace the package references: remove
Arc4u.Standard.DependencyandArc4u.Standard.Dependency.ComponentModel, then addArc4u.DependencyandArc4u.Dependency.Toolto every project that contains[Export]classes (see Install).Replace the container setup in
Program.cswith calls to the generated methods.Before (8.x)
using Arc4u.Dependency; using Arc4u.Dependency.ComponentModel; var builder = WebApplication.CreateBuilder(args); new ComponentModelContainer(builder.Services).InitializeFromConfig(builder.Configuration);After (9)
using Arc4u.Dependency; var builder = WebApplication.CreateBuilder(args); builder.Services.RegisterBusinessTypes(); // one call per project with [Export] classes builder.Services.RegisterApiTypes(); builder.Services.RegisterTypes(); // types listed in Configs/appsettings.jsonMove the
Application.Dependencysection:- Drop the
Assemblieslist. Each of your projects now registers its own classes through itsRegister<Name>Typesmethod. Instead ofRejectedTypes, remove[Export]from the classes you do not want registered. - Keep
RegisterTypesfor the types of other packages. Put the section inConfigs/appsettings.jsonof the host project, declare that file asAdditionalFiles, and write each entry as a string (see appsettings.json). - Rename the assembly part of each entry to its 9 name: 8.x entries name 8.x assemblies, for
example
"Arc4u.AppSettings, Arc4u.Standard.Configuration"becomes"Arc4u.AppSettings, Arc4u.Configuration"(see Package renames). An entry that still names an 8.x assembly is skipped with warningARC4UDEP003.
- Drop the
Replace
IContainerResolvewithIServiceProvider, or better, inject the services themselves (see the table below).Replace
IContainerRegistrycalls with theIServiceCollectionmethods of .NET (see the table below).
Replace IContainerResolve
Before (8.x)
public class CheckoutService
{
private readonly IPaymentGateway _gateway;
public CheckoutService(IContainerResolve container)
{
_gateway = container.Resolve<IPaymentGateway>("Stripe")!;
}
}
After (9)
using Arc4u.Dependency.Attribute;
using Microsoft.Extensions.DependencyInjection;
namespace Contoso.Api;
[Export, Scoped]
public class CheckoutService([FromKeyedServices("Stripe")] IPaymentGateway gateway)
{
public string Pay() => gateway.Name;
}
IContainerResolve (8.x) |
IServiceProvider (9) |
|---|---|
Resolve<T>() |
GetService<T>() |
Resolve<T>(name) |
GetKeyedService<T>(name) |
TryResolve<T>(out value) |
TryGetService<T>(out value) (Arc4u.Dependency) |
TryResolve<T>(name, out value) |
TryGetService<T>(name, out value) (Arc4u.Dependency) |
TryResolve(type, name, out value) |
TryGetService(type, name, out value) (Arc4u.Dependency) |
ResolveAll<T>() |
GetServices<T>() |
ResolveAll<T>(name) |
GetKeyedServices<T>(name) |
CreateScope() |
CreateScope(), then use scope.ServiceProvider |
The TryGetService methods are in the Arc4u.Dependency namespace
(ServiceProviderExtensions). Like TryResolve, they return false instead
of throwing, including when the service throws while it is created.
Replace IContainerRegistry
IContainerRegistry (8.x) |
IServiceCollection (9) |
|---|---|
Register<TFrom, To>() |
AddTransient<TFrom, To>() |
RegisterScoped<TFrom, To>() |
AddScoped<TFrom, To>() |
RegisterSingleton<TFrom, To>() |
AddSingleton<TFrom, To>() |
Register<TFrom, To>(name), RegisterScoped<TFrom, To>(name), RegisterSingleton<TFrom, To>(name) |
AddKeyedTransient<TFrom, To>(name), AddKeyedScoped<TFrom, To>(name), AddKeyedSingleton<TFrom, To>(name) |
RegisterInstance<T>(instance) |
AddSingleton<T>(instance) |
RegisterInstance<T>(instance, name) |
AddKeyedSingleton<T>(name, instance) |
RegisterFactory<T>(factory) |
AddTransient<T>(_ => factory()) |
RegisterScopedFactory<T>(factory), RegisterSingletonFactory<T>(factory) |
AddScoped<T>(_ => factory()), AddSingleton<T>(_ => factory()) |
CreateContainer() |
builder.Build() |
Troubleshooting
The type or namespace name 'IContainerResolve' could not be found
The type no longer exists. Inject the service itself, or IServiceProvider, as shown in
Replace IContainerResolve.
A service registered in 8.x is missing after the migration
- Its project does not reference
Arc4u.Dependency.Tool, orProgram.csdoes not call the project'sRegister<Name>Typesmethod. - It came from the
Assemblieslist ofApplication.Dependency, which is no longer read. - Its
RegisterTypesentry still names the 8.x assembly (Arc4u.Standard.*). Use the 9 assembly name (see Package renames). - It was listed in
RegisterTypesin the object form, or with aVersion=that does not match the referenced assembly; see Troubleshooting in the dependency injection guide.