Jonathan Wood
Jonathan Wood

Reputation: 67195

Dependency injection for settings class cannot be resolved

Okay, I'm obviously doing something stupid here but I can't seem to find it.

I'm writing my first Razor Pages app and created an EmailSender class, which implements IEmailSender. In addition, the EmailSender constructor accepts an EmailSettings parameter. EmailSettings is implemented as follows.

appsettings.json

"EmailSettings": {
  "Host": "****.*****.com",
  "Port": "587",
  "UserName": "****@**********.com",
  "Password": "***********",
  "FromAddress": "****@**********.com",
  "EnableSsl": "true"
},

EmailSettings.cs

public class EmailSettings
{
    public string Host { get; set; }
    public int Port { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
    public string FromAddress { get; set; }
    public bool EnableSsl { get; set; }
}

Startup.cs

public void ConfigureServices(IServiceCollection services)
{

    // ...

    services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));
    services.AddSingleton<IEmailSender, EmailSender>();
}

This follows the basic approach taken from an article I read. But when I run the app, I get an immediate exception:

System.AggregateException: 'Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Identity.UI.Services.IEmailSender Lifetime: Transient ImplementationType: Bamtok.Services.EmailSender': Unable to resolve service for type 'Bamtok.Services.EmailSettings' while attempting to activate 'Bamtok.Services.EmailSender'.)'

Somehow, it is not able to instantiate an EmailSettings instance to pass to the EmailSender constructor.

Can anyone see what I'm missing?

Upvotes: 2

Views: 612

Answers (1)

Nkosi
Nkosi

Reputation: 247018

Configure<T> registers IOptions<EmailSettings> and, as you stated, you injected EmailSettings into the dependent class.

You can either leave the class as is and refactor Startup

public void ConfigureServices(IServiceCollection services) {

    // ...

    EmailSettings settings = Configuration.GetSection("EmailSettings").Get<EmailSettings>();
    services.AddSingleton(settings);
    services.AddSingleton<IEmailSender, EmailSender>();
}

Or update the class to expect the IOptions<T> dependency

public class EmailSender : IEmailSender {
    private readonly EmailSettings emailSettings;

    public EmailSender(IOptions<EmailSettings> emailSettings) {
        this.emailSettings = emailSettings.Value;
    }

    //...
}

The difference between the former over the latter suggestion is that the implementation will not be coupled to framework concerns.

Upvotes: 1

Related Questions