C# – How to supply IOptions

The options pattern is an indirect way to dependency inject settings into a registered service. If you’re using code that implements the options pattern, then you’re required to supply an IOptions<T> object.

For example, let’s say you’re using the MovieService class and it has the following constructor:

public MovieService(IOptions<MovieSettings> options)
Code language: C# (cs)

This requires you to supply the IOptions<MovieSettings> parameter.

If the settings are in appsettings.json, you can use AddOptions().Bind():

public class Startup
{
	//rest of the class
	public void ConfigureServices(IServiceCollection services)
	{
		//rest of method

		services.AddOptions<MovieSettings>().Bind(Configuration.GetSection("MovieSettings"));
	}
}
Code language: C# (cs)

What if you want to supply hardcoded values, or if you want to fetch the settings from somewhere using a registered service (such as a database repository class)? In this article, I’ll show how to supply the IOptions<T> object in these scenarios.

Supply IOptions<T> with hardcoded values

When you’re using code that implements the options pattern, and you want to use hardcoded values, then you can register the Options<T> object and use Options.Create().

For example, let’s say you want to hardcode the MovieSettings values. Here’s how to supply IOptions<MovieSettings> with hardcoded values:

using Microsoft.Extensions.Options;

public class Startup
{
	//rest of the class
	public void ConfigureServices(IServiceCollection services)
	{
		//rest of method

		services.AddSingleton<IOptions<MovieSettings>>(_ =>
		{
			return Options.Create(new MovieSettings()
			{
				MovieAPIUrl = "https://localhost:12345/movies/api"
			});
		});
	}
}
Code language: C# (cs)

Supply IOptions<T> from a registered service

Let’s say you want to supply IOptions<MovieSettings> by querying the database using the registered MovieSettingsRepository service.

There are two approaches for doing that:

  • Use AddOptions<MovieSettings>().Configure<IMovieSettingsRepository>().
  • Register IOptions<MovieSettings> directly, allowing you to use Options.Create().

I’ll show both approaches below.

Approach 1 – Use AddOptions<MovieSettings>().Configure<IMovieSettingsRepository>()

With this overload of AddOptions().Configure(), you define a lambda that accepts the MovieSettings object and the resolved IMovieSettingsRepository instance. You can set properties on the MovieSettings object.

public class Startup
{
	public void ConfigureServices(IServiceCollection services)
	{
		services.AddSingleton<IMovieSettingsRepository, MovieSettingsRepository>();
		
		services.AddOptions<MovieSettings>()
		.Configure<IMovieSettingsRepository>((movieSettings, movieSettingsRepo) =>
		{
			movieSettings.MovieAPIUrl = movieSettingsRepo.GetSettings().MovieAPIUrl;
		});
	}
}
Code language: C# (cs)

This approach is good if you only want to set a few of the properties.

Approach 2 – Register IOptions<MovieSettings> directly, allowing you to use Options.Create()

You don’t need to use AddOptions(). You can register IOptions<MovieSettings> directly just like anything else. With this approach, you can resolve the IMovieSettingsRepository instance and use it to create the MovieSettings object and pass it to Options.Create().

using Microsoft.Extensions.Options;

public class Startup
{
        //rest of class
	public void ConfigureServices(IServiceCollection services)
	{
                //rest of method

		services.AddSingleton<IMovieSettingsRepository, MovieSettingsRepository>();
		
		services.AddSingleton<IOptions<MovieSettings>>(serviceProvider =>
		{
			var repo = serviceProvider.GetService<IMovieSettingsRepository>();
			return Options.Create(repo.GetSettings());
		});
	}
}
Code language: C# (cs)

This approach gives you full control over how the IOptions<MovieSettings> object is supplied.