Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clean way to specify options for a service when doing DI

Tags:

asp.net-core

So I have a service lets say for example it's an email service on ASPNET Core.

When I add my service to the ASPNET DI container I would like to apply the following pattern on my IServiceCollection to setup my service.

public interface IEmailService
{
    void SendMail(string recipient, string message);
}
public void ConfigureServices(IServiceCollection services)
{
    //configures my service
    services.AddEmailService<MyEmailService>(options => options.UseEmailServer(sender, smtpHost, smtpPort, smtpPassword));
}

I would like to know whats the best way to do this if possible. I am sure I would need to make an extension method for the .AddEmailService() method on IServiceCollection however anything beyond that I am not sure where to start or look.

like image 717
Lutando Avatar asked Feb 03 '16 13:02

Lutando


People also ask

How do I get IServiceCollection from IServiceProvider?

An instance of IServiceProvider itself can be obtained by calling a BuildServiceProvider method of an IServiceCollection . IServiceCollection is a parameter of ConfigureServices method in a Startup class. It seems to be magically called with an instance of IServiceCollection by the framework.

Which method of startup class configures or adds services required by the application?

The host provides services that are available to the Startup class constructor. The app adds additional services via ConfigureServices . Both the host and app services are available in Configure and throughout the app.

What is IServiceCollection in .NET core?

AddScoped(IServiceCollection, Type, Type) Adds a scoped service of the type specified in serviceType with an implementation of the type specified in implementationType to the specified IServiceCollection.

What is configure services in ASP NET core?

The ConfigureServices method is a place where you can register your dependent classes with the built-in IoC container. After registering dependent class, it can be used anywhere in the application. You just need to include it in the parameter of the constructor of a class where you want to use it.


1 Answers

Here's an example application with comments to let you know what the different things are doing:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Add the options stuff. This will allow you to inject IOptions<T>.
        services.AddOptions();

        // This will take care of adding and configuring the email service.
        services.AddEmailService<MyEmailService>(options =>
        {
            options.Host = "some-host.com";
            options.Port = 25;
            options.Sender = "[email protected]";

            options.Username = "email";
            options.Password = "sup4r-secr3t!";
        });
    }

    public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
    {
        // Make sure we add the console logger.
        loggerFactory.AddConsole();

        app.Use(async (context, next) =>
        {
            // Retrieve the email service from the services.
            var emailService = context.RequestServices.GetRequiredService<IEmailService>();

            // Send the email
            await emailService.SendMail("[email protected]", "Hello World!");
        });
    }

    public static void Main(string[] args)
    {
        WebApplication.Run<Startup>(args);
    }
}

public interface IEmailService
{
    Task SendMail(string recipient, string message);
}

public class EmailOptions
{
    public string Sender { get; set; }

    public string Host { get; set; }

    public int Port { get; set; }

    public string Username { get; set; }

    public string Password { get; set; }
}

public class MyEmailService : IEmailService
{
    public MyEmailService(IOptions<EmailOptions> options, ILogger<MyEmailService> logger)
    {
        Options = options; // This contains the instance we configured.
        Logger = logger;
    }

    private IOptions<EmailOptions> Options { get; }

    private ILogger<MyEmailService> Logger { get; }

    public Task SendMail(string recipient, string message)
    {
        // Send the email

        var builder = new StringBuilder();

        builder.AppendLine($"Host: {Options.Value.Host}");
        builder.AppendLine($"Port: {Options.Value.Port}");
        builder.AppendLine($"Username: {Options.Value.Username}");
        builder.AppendLine($"Password: {Options.Value.Password}");
        builder.AppendLine("---------------------");
        builder.AppendLine($"From: {Options.Value.Sender}");
        builder.AppendLine($"To: {recipient}");
        builder.AppendLine("---------------------");
        builder.AppendLine($"Message: {message}");

        Logger.LogInformation(builder.ToString());

        return Task.FromResult(0);
    }
}

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddEmailService<TEmailService>(this IServiceCollection services, Action<EmailOptions> configure)
        where TEmailService : class, IEmailService
    {
        // Configure the EmailOptions and register it in the service collection, as IOptions<EmailOptions>.
        services.Configure(configure);

        // Add the service itself to the collection.
        return services.AddSingleton<IEmailService, TEmailService>();
    }
}

And here's the application running in the console:

Application Running

As you can see, the application is pulling some information from the configured EmailOptions, and some information form the arguments passed in.

EDIT: These are the required packages:

"Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
"Microsoft.Extensions.OptionsModel": "1.0.0-rc1-final",
"Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final"
like image 53
khellang Avatar answered Sep 22 '22 15:09

khellang