将依赖项注入到 ASP.NET Core 中的控制器

作者:Rick Anderson

ASP.NET Core MVC 控制器通过构造函数显式请求依赖项。 ASP.NET Core内置支持 依赖注入 (DI)。 DI 使应用更易于测试和维护。

查看或下载示例代码 (如何下载)

构造函数注入

服务作为构造函数参数添加,并且运行时从服务容器中解析服务。 通常使用接口来定义服务。 例如,考虑需要当前时间的应用。 以下接口公开 IDateTime 服务:

public interface IDateTime
{
    DateTime Now { get; }
}

以下代码实现 IDateTime 接口:

public class SystemDateTime : IDateTime
{
    public DateTime Now
    {
        get { return DateTime.Now; }
    }
}

将服务添加到服务容器中:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IDateTime, SystemDateTime>();

    services.AddControllersWithViews();
}

有关 AddSingleton 的详细信息,请参阅 DI 服务生存期。

以下代码根据一天中的时间向用户显示问候语:

public class HomeController : Controller
{
    private readonly IDateTime _dateTime;

    public HomeController(IDateTime dateTime)
    {
        _dateTime = dateTime;
    }

    public IActionResult Index()
    {
        var serverTime = _dateTime.Now;
        if (serverTime.Hour < 12)
        {
            ViewData["Message"] = "It's morning here - Good Morning!";
        }
        else if (serverTime.Hour < 17)
        {
            ViewData["Message"] = "It's afternoon here - Good Afternoon!";
        }
        else
        {
            ViewData["Message"] = "It's evening here - Good Evening!";
        }
        return View();
    }

运行应用并且系统将根据时间显示一条消息。

使用 FromServices 的操作注入

FromServicesAttribute 允许将服务直接注入到操作方法,而无需使用构造函数注入:

public IActionResult About([FromServices] IDateTime dateTime)
{
    return Content( $"Current server time: {dateTime.Now}");
}

使用 FromKeyedServices 的操作注入

以下代码显示了如何使用 [FromKeyedServices] 属性从 DI 容器访问键控服务:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");
builder.Services.AddControllers();

var app = builder.Build();

app.MapControllers();

app.Run();

public interface ICache
{
    object Get(string key);
}
public class BigCache : ICache
{
    public object Get(string key) => $"Resolving {key} from big cache.";
}

public class SmallCache : ICache
{
    public object Get(string key) => $"Resolving {key} from small cache.";
}

[ApiController]
[Route("/cache")]
public class CustomServicesApiController : Controller
{
    [HttpGet("big")]
    public ActionResult<object> GetBigCache([FromKeyedServices("big")] ICache cache)
    {
        return cache.Get("data-mvc");
    }

    [HttpGet("small")]
    public ActionResult<object> GetSmallCache([FromKeyedServices("small")] ICache cache)
    {
        return cache.Get("data-mvc");
    }
}

属性注入 FromServices

FromServicesAttribute也可以应用于 MVC 控制器上的公共属性。 运行时在模型绑定期间解析并填充 DI 容器中的这些属性:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    [FromServices]
    public required ILogger<WeatherForecastController> Logger { get; set; }

    [FromServices]
    public required IWeatherService WeatherService { get; set; }

    [HttpGet]
    public IEnumerable<WeatherForecast> Get()
    {
        Logger.LogInformation("Fetching weather forecast");
        return WeatherService.GetForecast();
    }
}

注释

具有公共 setter 的控制器属性支持使用 [FromServices] 进行属性注入。 对于通常通过依赖注入容器解析的服务,不支持此功能。

从控制器访问设置

从控制器中访问应用或配置设置是一种常见模式。 选项模式 (Options pattern)在 ASP.NET Core 中的选项模式一文中进行了描述,是管理设置的首选方法。 通常情况下,不要直接将 IConfiguration 注入到控制器中。

创建表示选项的类。 例如:

public class SampleWebSettings
{
    public string Title { get; set; }
    public int Updates { get; set; }
}

将配置类添加到服务集合中:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IDateTime, SystemDateTime>();
    services.Configure<SampleWebSettings>(Configuration);

    services.AddControllersWithViews();
}

将应用配置为从 JSON 格式文件中读取设置:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.AddJsonFile("samplewebsettings.json",
                    optional: false,
                    reloadOnChange: true);
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

以下代码从服务容器请求 IOptions<SampleWebSettings> 设置,并通过 Index 方法使用它们:

public class SettingsController : Controller
{
    private readonly SampleWebSettings _settings;

    public SettingsController(IOptions<SampleWebSettings> settingsOptions)
    {
        _settings = settingsOptions.Value;
    }

    public IActionResult Index()
    {
        ViewData["Title"] = _settings.Title;
        ViewData["Updates"] = _settings.Updates;
        return View();
    }
}

控制器即服务

默认情况下,ASP.NET Core不会将控制器注册为 DI 容器中的服务。 运行时使用 DefaultControllerActivator 创建控制器实例,并从 DI 容器解析服务用于构造函数参数,但控制器本身并非从容器中解析而来。

调用 AddControllersAsServices 会将所有控制器注册为 DI 容器中的服务:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews().AddControllersAsServices();

将控制器注册为服务可实现:

  • 使用自定义 IControllerActivator截获控制器创建。
  • 对控制器使用任何 DI 生存期管理。
  • 使用任何已注册的构造函数将服务注入控制器,因为 DI 容器选择构造函数。

注释

在调用 AddControllersAsServices之前,先配置 ApplicationPartManager。 有关详细信息,请参阅 Razor。

作者:Rick Anderson 和 Steve Smith

ASP.NET Core MVC 控制器通过构造函数显式请求依赖项。 ASP.NET Core内置支持 依赖注入 (DI)。 DI 使应用更易于测试和维护。

查看或下载示例代码 (如何下载)

构造函数注入

服务作为构造函数参数添加,并且运行时从服务容器中解析服务。 通常使用接口来定义服务。 例如,考虑需要当前时间的应用。 以下接口公开 IDateTime 服务:

public interface IDateTime
{
    DateTime Now { get; }
}

以下代码实现 IDateTime 接口:

public class SystemDateTime : IDateTime
{
    public DateTime Now
    {
        get { return DateTime.Now; }
    }
}

将服务添加到服务容器中:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IDateTime, SystemDateTime>();

    services.AddControllersWithViews();
}

有关 AddSingleton 的详细信息,请参阅 DI 服务生存期。

以下代码根据一天中的时间向用户显示问候语:

public class HomeController : Controller
{
    private readonly IDateTime _dateTime;

    public HomeController(IDateTime dateTime)
    {
        _dateTime = dateTime;
    }

    public IActionResult Index()
    {
        var serverTime = _dateTime.Now;
        if (serverTime.Hour < 12)
        {
            ViewData["Message"] = "It's morning here - Good Morning!";
        }
        else if (serverTime.Hour < 17)
        {
            ViewData["Message"] = "It's afternoon here - Good Afternoon!";
        }
        else
        {
            ViewData["Message"] = "It's evening here - Good Evening!";
        }
        return View();
    }

运行应用并且系统将根据时间显示一条消息。

使用 FromServices 的操作注入

FromServicesAttribute 允许将服务直接注入到操作方法,而无需使用构造函数注入:

public IActionResult About([FromServices] IDateTime dateTime)
{
    return Content( $"Current server time: {dateTime.Now}");
}

从控制器访问设置

从控制器中访问应用或配置设置是一种常见模式。 选项模式 (Options pattern)在 ASP.NET Core 中的选项模式一文中进行了描述,是管理设置的首选方法。 通常情况下,不要直接将 IConfiguration 注入到控制器中。

创建表示选项的类。 例如:

public class SampleWebSettings
{
    public string Title { get; set; }
    public int Updates { get; set; }
}

将配置类添加到服务集合中:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IDateTime, SystemDateTime>();
    services.Configure<SampleWebSettings>(Configuration);

    services.AddControllersWithViews();
}

将应用配置为从 JSON 格式文件中读取设置:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.AddJsonFile("samplewebsettings.json",
                    optional: false,
                    reloadOnChange: true);
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

以下代码从服务容器请求 IOptions<SampleWebSettings> 设置,并通过 Index 方法使用它们:

public class SettingsController : Controller
{
    private readonly SampleWebSettings _settings;

    public SettingsController(IOptions<SampleWebSettings> settingsOptions)
    {
        _settings = settingsOptions.Value;
    }

    public IActionResult Index()
    {
        ViewData["Title"] = _settings.Title;
        ViewData["Updates"] = _settings.Updates;
        return View();
    }
}

控制器即服务

默认情况下,ASP.NET Core不会将控制器注册为 DI 容器中的服务。 运行时使用 DefaultControllerActivator 创建控制器实例,并从 DI 容器解析服务用于构造函数参数,但控制器本身并非从容器中解析而来。

调用 AddControllersAsServices 会将所有控制器注册为 DI 容器中的服务:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews().AddControllersAsServices();
}

将控制器注册为服务可实现:

  • 使用自定义 IControllerActivator截获控制器创建。
  • 对控制器使用任何 DI 生存期管理。
  • 使用任何已注册的构造函数将服务注入控制器,因为 DI 容器选择构造函数。

注释

在调用 AddControllersAsServices之前,先配置 ApplicationPartManager。 有关详细信息,请参阅 Razor。

其他资源

ASP.NET Core MVC 控制器通过构造函数显式请求依赖项。 ASP.NET Core内置支持 依赖注入 (DI)。 DI 使应用更易于测试和维护。

查看或下载示例代码 (如何下载)

构造函数注入

服务作为构造函数参数添加,并且运行时从服务容器中解析服务。 通常使用接口来定义服务。 例如,考虑需要当前时间的应用。 以下接口公开 IDateTime 服务:

public interface IDateTime
{
    DateTime Now { get; }
}

以下代码实现 IDateTime 接口:

public class SystemDateTime : IDateTime
{
    public DateTime Now
    {
        get { return DateTime.Now; }
    }
}

将服务添加到服务容器中:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IDateTime, SystemDateTime>();

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

有关 AddSingleton 的详细信息,请参阅 DI 服务生存期。

以下代码根据一天中的时间向用户显示问候语:

public class HomeController : Controller
{
    private readonly IDateTime _dateTime;

    public HomeController(IDateTime dateTime)
    {
        _dateTime = dateTime;
    }

    public IActionResult Index()
    {
        var serverTime = _dateTime.Now;
        if (serverTime.Hour < 12)
        {
            ViewData["Message"] = "It's morning here - Good Morning!";
        }
        else if (serverTime.Hour < 17)
        {
            ViewData["Message"] = "It's afternoon here - Good Afternoon!";
        }
        else
        {
            ViewData["Message"] = "It's evening here - Good Evening!";
        }
        return View();
    }

运行应用并且系统将根据时间显示一条消息。

使用 FromServices 的操作注入

FromServicesAttribute 允许将服务直接注入到操作方法,而无需使用构造函数注入:

public IActionResult About([FromServices] IDateTime dateTime)
{
    ViewData["Message"] = $"Current server time: {dateTime.Now}";

    return View();
}

从控制器访问设置

从控制器中访问应用或配置设置是一种常见模式。 选项模式 (Options pattern)在 ASP.NET Core 中的选项模式一文中进行了描述,是管理设置的首选方法。 通常情况下,不要直接将 IConfiguration 注入到控制器中。

创建表示选项的类。 例如:

public class SampleWebSettings
{
    public string Title { get; set; }
    public int Updates { get; set; }
}

将配置类添加到服务集合中:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IDateTime, SystemDateTime>();
    services.Configure<SampleWebSettings>(Configuration);
    
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

将应用配置为从 JSON 格式文件中读取设置:

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.AddJsonFile("samplewebsettings.json", 
                                optional: false,        // File is not optional.
                                reloadOnChange: false);
        })
        .UseStartup<Startup>();
}

以下代码从服务容器请求 IOptions<SampleWebSettings> 设置,并通过 Index 方法使用它们:

public class SettingsController : Controller
{
    private readonly SampleWebSettings _settings;

    public SettingsController(IOptions<SampleWebSettings> settingsOptions)
    {
        _settings = settingsOptions.Value;
    }

    public IActionResult Index()
    {
        ViewData["Title"] = _settings.Title;
        ViewData["Updates"] = _settings.Updates;
        return View();
    }
}

控制器即服务

默认情况下,ASP.NET Core不会将控制器注册为 DI 容器中的服务。 运行时使用 DefaultControllerActivator 创建控制器实例,并从 DI 容器解析服务用于构造函数参数,但控制器本身并非从容器中解析而来。

调用 AddControllersAsServices 会将所有控制器注册为 DI 容器中的服务:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().AddControllersAsServices();
}

将控制器注册为服务可实现:

  • 使用自定义 IControllerActivator截获控制器创建。
  • 对控制器使用任何 DI 生存期管理。
  • 使用任何已注册的构造函数将服务注入控制器,因为 DI 容器选择构造函数。

注释

在调用 AddControllersAsServices之前,先配置 ApplicationPartManager。 有关详细信息,请参阅 Razor。

其他资源