Logo Microsoft
Agents & OrchestrationSûr100100/100

Migrating Mvc Dependency Injection (Microsoft)

Migrates dependency injection configuration from ASP.NET Framework MVC and WebAPI projects to ASP.NET Core built-in DI or modernized third-party container integration. Use when upgrading projects that use DependencyResolver.SetResolver, config.DependencyResolver, custom IControllerFactory, custom IHttpControllerActivator, ServiceLocator.Current, or third-party containers (Autofac, Unity, Ninject, Castle Windsor). Also triggers for "migrate dependency injection", "convert DI container", "replace DependencyResolver", PerRequest lifetime mapping, IControllerActivator migration, and property injection patterns.

ou envoie-le directement à ton agent.

Installer dans ton projet

$ curl -fsSL https://arboristhree.xyz/cli/install-skill.mjs -o /tmp/arboris-install-skill.mjs && node /tmp/arboris-install-skill.mjs migrating-mvc-dependency-injection__microsoft-upgrade-agent-plugins__plugins-upgrade-agent-extenders-upgrade-dotnet-upgrade-skills-lazy-web-mvc-e9cab2362f89
Logo Microsoft
Microsoft
microsoft/upgrade-agent-plugins
Contenu à copier
---

name: migrating-mvc-dependency-injection
description: >
  Migrates dependency injection configuration from ASP.NET Framework MVC and WebAPI projects to
  ASP.NET Core built-in DI or modernized third-party container integration. Use when upgrading
  projects that use DependencyResolver.SetResolver, config.DependencyResolver, custom
  IControllerFactory, custom IHttpControllerActivator, ServiceLocator.Current, or third-party
  containers (Autofac, Unity, Ninject, Castle Windsor). Also triggers for "migrate dependency
  injection", "convert DI container", "replace DependencyResolver", PerRequest lifetime mapping,
  IControllerActivator migration, and property injection patterns.
metadata:
  origin: Microsoft
  traits: .NET|CSharp|VisualBasic|DotNetCore
  discovery: lazy
---


# ASP.NET MVC/WebAPI Dependency Injection Migration

## Overview

Migrate `DependencyResolver`-based DI from ASP.NET MVC and WebAPI to ASP.NET Core's built-in `IServiceCollection`/`IServiceProvider` pattern, or modernize third-party container integration. ASP.NET Core has DI built into the framework — the `DependencyResolver` and `IDependencyScope` APIs no longer exist.

> **Related skills:**
> - To remove Autofac entirely and use built-in DI: see `migrating-autofac-to-dotnet-di`
> - To keep Autofac but modernize its integration: see `integrating-autofac-with-dotnet`

## Workflow

```
Migration Progress:
- [ ] Step 1: Inventory DI usage
- [ ] Step 2: Map service registrations and lifetimes
- [ ] Step 3: Register services in Program.cs
- [ ] Step 4: Migrate or remove third-party container
- [ ] Step 5: Migrate controller factory customizations
- [ ] Step 6: Eliminate Service Locator usage
- [ ] Step 7: Remove obsolete DI code
- [ ] Step 8: Build and verify
```

### Step 1: Inventory DI Usage

Search the project for these patterns to determine the migration scope:

| Pattern | Indicates |
|---------|-----------|
| `DependencyResolver.SetResolver` | MVC DI resolver |
| `config.DependencyResolver =` or `GlobalConfiguration.Configuration.DependencyResolver` | WebAPI DI resolver |
| `ContainerBuilder`, `IUnityContainer`, `IKernel`, `IWindsorContainer` | Third-party container |
| `ServiceLocator.Current` | Service Locator anti-pattern |
| `IControllerFactory` | Custom MVC controller factory |
| `IHttpControllerActivator` | Custom WebAPI controller activator |
| Property injection (`[Dependency]`, `InjectProperty`) | Property injection patterns |

Record every service registration and its lifetime before changing anything.

### Step 2: Map Service Registrations and Lifetimes

Document all service registrations from the existing container configuration. Map each lifetime to its ASP.NET Core equivalent:

| Framework Lifetime | ASP.NET Core | Method |
|--------------------|-------------|--------|
| Per-request / `InstancePerRequest` / `HierarchicalLifetimeManager` | Scoped | `AddScoped<TService, TImpl>()` |
| Singleton / `SingleInstance` / `ContainerControlledLifetimeManager` | Singleton | `AddSingleton<TService, TImpl>()` |
| Transient / `InstancePerDependency` / `TransientLifetimeManager` | Transient | `AddTransient<TService, TImpl>()` |
| Per-thread / `PerThreadLifetimeManager` | Scoped | `AddScoped<TService, TImpl>()` |
| `ExternallyControlledLifetimeManager` | Transient | `AddTransient<TService, TImpl>()` |

Per-thread lifetime maps to Scoped because ASP.NET Core processes each request on a single thread from the thread pool, making the semantics equivalent for web scenarios.

### Step 3: Register Services in Program.cs

Move all service registrations to `builder.Services` in `Program.cs`. Create an extension method to keep `Program.cs` clean when there are many registrations:

**Before** (Global.asax.cs or App_Start):
```csharp
var container = new UnityContainer();
container.RegisterType<IOrderService, OrderService>(new HierarchicalLifetimeManager());
container.RegisterType<IRepository, SqlRepository>(new TransientLifetimeManager());
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
```

**After** (Program.cs):
```csharp
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.RegisterApplicationServices();

// Extension method in a separate file
public static class ServiceRegistration
{
    public static IServiceCollection RegisterApplicationServices(this IServiceCollection services)
    {
        services.AddScoped<IOrderService, OrderService>();
        services.AddTransient<IRepository, SqlRepository>();
        return services;
    }
}
```

If `HttpContext.Current` was used anywhere in the project, register the accessor:

```csharp
builder.Services.AddHttpContextAccessor();
```

### Step 4: Migrate or Remove Third-Party Container

Choose the appropriate path based on the container in use and the desired outcome.

#### Option A: Replace with built-in DI (recommended for simple registrations)

Remove the third-party container entirely. Map all registrations to `IServiceCollection` using the lifetime table in Step 2. Remove all container-specific NuGet packages.

#### Option B: Keep the third-party container with Core integration

When the project relies on advanced container features (modules, decorators, interceptors, child containers), keep the container but modernize the integration.

**Autofac:**
```csharp
// Install: Autofac.Extensions.DependencyInjection
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder =>
{
    containerBuilder.RegisterModule<MyModule>();
});
```

**Unity:**
```csharp
// Install: Unity.Microsoft.DependencyInjection
builder.Host.UseUnityServiceProvider(container =>
{
    container.RegisterType<IMyService, MyService>();
});
```

**Castle Windsor:**
```csharp
// Install: Castle.Windsor.MsDependencyInjection
var windsorContainer = new WindsorContainer();
windsorContainer.Install(FromAssembly.This());
builder.Host.UseServiceProviderFactory(
    new WindsorServiceProviderFactory(windsorContainer));
```

**Ninject:** No official ASP.NET Core integration exists. Migrate all registrations to built-in DI (Option A) or switch to Autofac. Do not attempt to create a custom adapter — the `IServiceProvider` contract has subtleties that break under a naive wrapper.

### Step 5: Migrate Controller Factory Customizations

ASP.NET Core replaces both `IControllerFactory` (MVC) and `IHttpControllerActivator` (WebAPI) with `IControllerActivator`.

If the custom factory only existed to enable constructor injection, remove it entirely — ASP.NET Core injects constructor dependencies into controllers by default.

If the factory contains custom logic (e.g., selecting controller types dynamically, applying cross-cutting concerns):

**Before** (MVC):
```csharp
public class CustomControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(
        RequestContext requestContext, Type controllerType)
    {
        // Custom logic here
        return (IController)_container.Resolve(controllerType);
    }
}
```

**After:**
```csharp
public class CustomControllerActivator : IControllerActivator
{
    public object Create(ControllerContext context)
    {
        var controllerType = context.ActionDescriptor.ControllerTypeInfo.AsType();
        // Custom logic here
        return context.HttpContext.RequestServices.GetRequiredService(controllerType);
    }

    public void Release(ControllerContext context, object controller)
    {
        (controller as IDisposable)?.Dispose();
    }
}

// Register in Program.cs
builder.Services.AddSingleton<IControllerActivator, CustomControllerActivator>();
```

### Step 6: Eliminate Service Locator Usage

Replace all `ServiceLocator.Current.GetInstance<T>()` calls with constructor injection. The Service Locator pattern hides dependencies and makes testing difficult — ASP.NET Core does not support it.

**Before:**
```csharp
public class OrderProcessor
{
    public void Process()
    {
        var service = ServiceLocator.Current.GetInstance<IOrderService>();
        service.Execute();
    }
}
```

**After:**
```csharp
public class OrderProcessor
{
    private readonly IOrderService _orderService;

    public OrderProcessor(IOrderService orderService)
    {
        _orderService = orderService;
    }

    public void Process()
    {
        _orderService.Execute();
    }
}
```

For locations where constructor injection is not possible (e.g., static methods, legacy code paths that cannot be refactored immediately), inject `IServiceProvider` and resolve explicitly as a temporary measure:

```csharp
var service = serviceProvider.GetRequiredService<IOrderService>();
```

Mark these as technical debt with a TODO comment — they should eventually be refactored to constructor injection.

### Step 7: Remove Obsolete DI Code

Remove all Framework-specific DI artifacts:

- `DependencyResolver.SetResolver(...)` calls
- `GlobalConfiguration.Configuration.DependencyResolver = ...` assignments
- Custom `IDependencyResolver` implementations
- Custom `IDependencyScope` implementations
- `ServiceLocator.SetLocatorProvider(...)` calls
- `CommonServiceLocator` package reference
- Third-party container packages (if Option A was chosen in Step 4)

### Step 8: Build and Verify

Build the project and verify:

1. All services resolve at startup — missing registrations cause `InvalidOperationException` at first request
2. Scoped services are not injected into singletons — this is a common lifetime mismatch bug that ASP.NET Core validates by default in Development environment
3. `IHttpContextAccessor` is registered if any service depends on `HttpContext`

## Property Injection

The built-in container does not support property injection. If the project used property injection (Autofac `PropertiesAutowired()`, Unity `[Dependency]` attribute, Ninject `[Inject]` attribute):

1. **Preferred:** Convert to constructor injection — add the dependency as a constructor parameter
2. **If constructor injection is impractical** (e.g., deep inheritance hierarchies): keep a third-party container that supports property injection (Autofac with `PropertiesAutowired()` works with ASP.NET Core integration)

## Success Criteria

- No `DependencyResolver`, `IDependencyScope`, or `ServiceLocator` references remain
- All service lifetimes correctly mapped per the lifetime table
- Third-party container either removed or integrated via `UseServiceProviderFactory`
- Custom controller factory logic migrated to `IControllerActivator` or removed
- No property injection without explicit third-party container support
- `IHttpContextAccessor` registered if `HttpContext` access is needed outside controllers
- Project builds without errors
- Services resolve correctly at runtime
Colle ce Markdown dans ton agent ou utilise les boutons ci-dessus pour l’écrire dans ton projet.

Ce que fait Migrating Mvc Dependency Injection (Microsoft)

[ ] Step 1: Inventory DI usage
[ ] Step 2: Map service registrations and lifetimes
[ ] Step 3: Register services in Program.cs
[ ] Step 4: Migrate or remove third-party container

Comment utiliser Migrating Mvc Dependency Injection (Microsoft)

1
Copie le prompt
Un clic copie le prompt packagé (ou l'envoie à ton agent).
2
L'agent installe le skill
Il ajoute le SKILL.md et ses ressources à ton projet.
3
Activation automatique
Le skill s'active dès que le contexte correspond.

Déclencheurs pour Migrating Mvc Dependency Injection (Microsoft)

Dis simplement à ton agent quelque chose comme :

Applique le skill migrating-mvc-dependency-injection__microsoft-upgrade-agent-plugins__plugins-upgrade-agent-extenders-upgrade-dotnet-upgrade-skills-lazy-web-mvc-e9cab2362f89 à cette tâche
Utilise migrating-mvc-dependency-injection__microsoft-upgrade-agent-plugins__plugins-upgrade-agent-extenders-upgrade-dotnet-upgrade-skills-lazy-web-mvc-e9cab2362f89 pour améliorer cette implémentation
Passe en revue ce sujet avec migrating-mvc-dependency-injection__microsoft-upgrade-agent-plugins__plugins-upgrade-agent-extenders-upgrade-dotnet-upgrade-skills-lazy-web-mvc-e9cab2362f89

Skills liés à Migrating Mvc Dependency Injection (Microsoft)