ASP.Net Core MVC 依赖注入无法工作

23

我正在尝试将一个接口注入到我的HomeController中,但是我遇到了以下错误:

InvalidOperationException: 在尝试激活时无法解析类型'Microsoft.Extensions.Configuration.IConfiguration'的服务

我的Startup类如下所示:

public Startup(IApplicationEnvironment appEnv)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(appEnv.ApplicationBasePath)
        .AddEnvironmentVariables()
        .AddJsonFile("appsettings.json");
    Configuration = builder.Build();
}

public IConfigurationRoot Configuration { get; set; }

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddApplicationInsightsTelemetry(Configuration);

    services.AddEntityFramework()
        .AddSqlServer()
        .AddDbContext<ApplicationDbContext>(options => options
            .UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddMvc();
    services.AddSingleton(provider => Configuration);

    // Add application services.
    services.AddTransient<IEmailSender, AuthMessageSender>();
    services.AddTransient<ISmsSender, AuthMessageSender>();
}

public void Configure(
    IApplicationBuilder app, 
    IHostingEnvironment env, 
    ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    app.UseApplicationInsightsRequestTelemetry();

    if (env.IsDevelopment())
    {
        app.UseBrowserLink();
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");

        try
        {
            using (var serviceScope = app.ApplicationServices
                .GetRequiredService<IServiceScopeFactory>()
                .CreateScope())
            {
                serviceScope.ServiceProvider
                        .GetService<ApplicationDbContext>()
                        .Database.Migrate();
            }
        }
        catch { }
    }

    app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear());

    app.UseApplicationInsightsExceptionTelemetry();
    app.UseStaticFiles();
    app.UseIdentity();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });

    app.Run((async (context) =>
    {
        await context.Response.WriteAsync("Error");
    }));
}

我的HomeController构造函数是:

public HomeController(IConfiguration configuration, IEmailSender mailService)
{
    _mailService = mailService;
    _to = configuration["emailAddress.Support"];
}

请告诉我我哪里错了。

Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.PopulateCallSites(ServiceProvider provider, ISet`1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)


3
我建议通过创建一个 POCO(普通的 C# 对象)来存储想要传递的任何配置细节,并注入该对象,从而将你的“Configuration”与“Controller”分离。 - stephen.vakil
如果构造函数有对象作为参数,但服务配置为接口,则可能会发生类似的错误。https://dev59.com/c1gR5IYBdhLWcg3ww_oz - Michael Freidgeim
3个回答

36

尝试将其注入为 IConfigurationRoot 而不是 IConfiguration:

 public HomeController(IConfigurationRoot configuration
    , IEmailSender mailService)
{
    _mailService = mailService;
    _to = configuration["emailAddress.Support"];
}

在这种情况下,该行

services.AddSingleton(provider => Configuration);

等同于

services.AddSingleton<IConfigurationRoot>(provider => Configuration);

因为类上的 Configuration 属性被声明为这样,在注入时将会匹配它注册的类型。我们可以轻松地复制这个过程,这可能会让它更清晰:

public interface IParent { }

public interface IChild : IParent { }

public class ConfigurationTester : IChild { }
public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddMvc();

    IChild example = new ConfigurationTester();
    services.AddSingleton(provider => example);
}
public class HomeController : Controller
{
    public HomeController(IParent configuration)
    {
        // this will blow up
    }
}

然而

正如stephen.vakil在评论中提到的那样,最好将配置文件加载到一个类中,然后根据需要将该类注入控制器。代码应该是这样的:

services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));

您可以使用 IOptions 接口获取这些���置:

public HomeController(IOptions<AppSettings> appSettings)

3
为什么?我在我的代码中并没有这样做,但它仍然可以正常工作。而且文档中也没有按照这个顺序进行:http://docs.asp.net/en/latest/fundamentals/dependency-injection.html - stephen.vakil
@stephen.vakil 你说得对,我在那个细节上完全错了。谢谢你提供的链接!我已经更新了我的答案,Jam。 - Will Ray
就是这样!你能解释一下有什么区别吗? - JamTay317
1
将其作为“IConfiguration”的注入,以获得更大的灵活性。 - Eilon

9

在Core 2.0中,建议使用IConfiguration而不是IConfigurationRoot

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}

public IConfiguration Configuration { get; }

来自https://learn.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/#add-configuration-providers的内容:

ASP.NET Core 2.0引入了新的配置API,它使得在应用程序中添加和使用配置变得更加容易。在1.x版本中,开发人员需要手动注册配置提供程序,然后将其注入到应用程序中。但是,在2.0版本中,开发人员只需要在Startup.cs文件中调用AddConfiguration()方法即可向应用程序添加配置提供程序。这个方法会自动注册内置的配置提供程序,如appsettings.json和appsettings.[Environment].json。


2

将项目从.Net Core 1.x迁移到2.0时,需要将所有的IConfigurationRoot更改为IConfiguration


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