.NET Core 2.0中无法在启动构造函数中注入自己的对象

7
我正在尝试使用依赖注入来注入自己的类,如下所示:

// Startup.cs
public class Startup
{
    private readonly ILogger _log;
    private readonly IMainController _controller;
    public Startup(ILoggerFactory loggerFactory, IMainController controller)
    {
        _log = loggerFactory.CreateLogger("Logger");
        _controller = controller;
    }

    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddScoped<IMainController, MainController>();
        // services.AddTransient<MainController, MainController>();
    }

然后是要注入的对象MainController

// MainController.cs
public interface IMainController
{
    Task Run(HttpContext context);
}
public class MainController : IMainController
{
    private readonly ILogger _log;

    public MainController(ILoggerFactory loggerFactory)
    {
        _log = loggerFactory.CreateLogger("Logger");
    }

在运行时,我收到以下错误:

未处理的异常:System.InvalidOperationException:在尝试激活'mtss.ws.Startup'时,无法解析mtss.ws.IMainController类型的服务。 在Microsoft.Extensions.Internal.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider provider) 在Microsoft.Extensions.Internal.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)期间

我想在MainController中注入ILoggerFactory(就像在Startup中一样),然后在Startup中注入一个新创建的MainController...

1
你正在创建什么类型的项目?ASP.Net,控制台... - Mr Slim
这是一个使用“dotnet new web”命令创建的“ASP.NET Core Empty”项目。 - opensas
4个回答

10
除了@nkosi在https://dev59.com/16Tja4cB1Zd3GeqPINgq#46013224的答案,您还可以使用WebHostBuilder ConfigureServices方法添加依赖项,如文档所述:

WebHostBuilder ConfigureServices方法添加的任何服务都可以由Startup类构造函数或其Configure方法请求。 在启动方法期间使用WebHostBuilder提供任何需要的服务。

代码示例如下:

// ...other code removed for brevity
// in Program.cs
public static void Main(string[] args)
{
    BuildWebHost(args).Run();
}

public static IWebHost BuildWebHost(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .ConfigureServices(services =>
            services.AddScoped<IMainController, MainController>()
        )
        .UseStartup<Startup>()
        .Build();

然后在 Startup.cs 文件中,我可以按照以下方式操作:

private readonly ILogger _log;
private readonly IMainController _controller;

public Startup(ILoggerFactory loggerFactory, IMainController controller)
{
    _log = loggerFactory.CreateLogger("Logger");
    _controller = controller;
}

这个 gist 帮助我理解了它,它有许多有用的例子。

7
根据文档,您应注意以下内容。

启动时可用的服务

ASP.NET Core 依赖注入提供了在应用程序启动期间使用的服务。您可以通过在 Startup 类的构造函数或其 Configure 方法中包含适当的接口作为参数来请求这些服务。ConfigureServices 方法仅接受一个 IServiceCollection 参数(但可以从此集合检索任何已注册的服务,因此不需要其他参数)。

下面是 Startup 方法通常请求的一些服务:

  • 在构造函数中:IHostingEnvironmentILogger<Startup>
  • 在 ConfigureServices 方法中:IServiceCollection
  • 在 Configure 方法中:IApplicationBuilderIHostingEnvironmentILoggerFactory

任何由 WebHostBuilder 的 ConfigureServices 方法添加的服务都可以从 Startup 类构造函数或其 Configure 方法中请求。使用 WebHostBuilder 提供在 Startup 方法中所需的任何服务。

当调用Startup的构造函数时,您正在尝试解决一个不可用的服务。IMainController在调用构造函数时尚未注册。但是,当调用Configure时,它应该可用,这为注入自定义服务提供了机会,因为它在调用ConfigureServices之后被调用,并且截至RTM,将为Configure方法创建一个作用域服务提供程序。
// Startup.cs
public class Startup {
    private ILogger _log;
    private IMainController _controller;

    public Startup() {

    }

    public void ConfigureServices(IServiceCollection services) {
        services.AddScoped<IMainController, MainController>();
        // services.AddTransient<MainController, MainController>();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, 
               ILoggerFactory loggerFactory, IMainController controller) {
        _log = loggerFactory.CreateLogger("Logger");
        _controller = controller;
        //...other code removed for brevity
    }
}

以上应该在 .net-core 2.0 中起作用


很好的答案,另一个选项是在WebHostBuilder的ConfigureServices方法中定义依赖项(请参见https://dev59.com/16Tja4cB1Zd3GeqPINgq#46015231)。 - opensas
@opensas,根据文档中的引用所述,您是正确的。由于不知道您如何应用“startup”,因此选择了最有可能从Core 1.*中学到的选项。 - Nkosi

1
你的设置是不可能的。在你的ConfigureServices方法之前,Startup的构造函数将会运行,这意味着你正在尝试在依赖注入注册之前注入IMainController

感谢您的反馈,@MathiasLykkegaardLorenzen。您是在说可以在“Startup.ConfigureServices”中注册服务并在“Startup”构造函数中接收它的实例吗?除非现在已经可以在构造函数运行之前调用实例方法,否则我不确定我的答案的哪个部分是错误的。 - Kirk Larkin

1
我会假设您正在构建一个ASP.Net Core2.0 MVC应用程序(但是您的MainController没有继承自Controller,所以我对此表示怀疑)。您不需要在DI容器中注册控制器。您拥有的构造函数应该足以让ASP.Net为您注入ILogger具体实例。

public MainController(ILoggerFactory loggerFactory)

如果您想将自己的服务添加到控制器中,则控制器的构造函数将更改为:

Controller

public MainController(ILoggerFactory loggerFactory, IMyService myService)

您在Startup.cs中的服务注册可能如下所示:

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddScoped<IMyService, MyService>();
}

我的服务
public interface IMyService
{
    void DoSomethingPLEASE();
}
public class MyService : IMyService
{
    public void DoSomethingPLEASE()
    {
        // Do Something PLEASE, ANYTHING!
    }
}

HomeController
public class HomeController : Controller
{
    public HomeController(ILoggerFactory loggerFactory, IMyService myServce)
    {
        myServce.DoSomethingPLEASE();
    }
    public IActionResult Index()
    {
        return View();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接