{"id":1313,"date":"2018-01-29T10:00:06","date_gmt":"2018-01-29T08:00:06","guid":{"rendered":"http:\/\/tpodolak.com\/blog\/?p=1313"},"modified":"2018-02-28T22:33:45","modified_gmt":"2018-02-28T20:33:45","slug":"asp-net-core-make-sure-ioptionst-initialized","status":"publish","type":"post","link":"https:\/\/tpodolak.com\/blog\/2018\/01\/29\/asp-net-core-make-sure-ioptionst-initialized\/","title":{"rendered":"ASP.NET Core &#8211; make sure <i>IOptions&lt;T&gt;<\/i> is initialized"},"content":{"rendered":"<h3>1. Introduction<\/h3>\n<p>As most people know <i>IOptions&lt;T&gt;<\/i> is a convenient way of handling configuration options in your application. Even though I&#8217;ve been using it for quite some time, last week I was unpleasantly surprised by a production bug caused by wrong usage of this mechanism.<\/p>\n<h3>2. Problem<\/h3>\n<p>Let&#8217;s say we have a simple controller which depends on two instances of <i>IOptions&lt;T&gt;<\/i><\/p>\n<pre lang=\"csharp\">\r\n[Route(\"api\/[controller]\")]\r\npublic class ValuesController : Controller\r\n{\r\n    private readonly IOptions<PaymentOptions> _paymentOptions;\r\n    private readonly IOptions<NotificationOptions> _notificationOptions;\r\n\r\n    public ValuesController(IOptions<PaymentOptions> paymentOptions, IOptions<NotificationOptions> notificationOptions)\r\n    {\r\n        _paymentOptions = paymentOptions ?? throw new ArgumentNullException(nameof(paymentOptions));\r\n        _notificationOptions = notificationOptions ?? throw new ArgumentNullException(nameof(notificationOptions));\r\n    }\r\n\r\n    \/\/ GET api\/values\r\n    [HttpGet]\r\n    public IEnumerable<string> Get()\r\n    {\r\n        var notificationOptions = _notificationOptions.Value;\r\n        var paymentOptions = _paymentOptions.Value;\r\n            \r\n        return new string[] {\"value1\", \"value2\"};\r\n    }\r\n}\r\n<\/pre>\n<p>Then we add only <i>IOptions<\/i> in Startup class &#8211; <i>IOptions&lt;PaymentOptoins&gt;<\/i><\/p>\n<pre lang=\"csharp\">\r\npublic class Startup\r\n{\r\n    \/\/ Rest of the code omitted for brevity\r\n    \/\/ This method gets called by the runtime. Use this method to add services to the container.\r\n    public void ConfigureServices(IServiceCollection services)\r\n    {\r\n        services.AddOptions();\r\n        services.AddMvc();\r\n        services.Configure<PaymentOptions>(Configuration.GetSection(\"PaymentOptions\"));\r\n    }\r\n      \/\/ Rest of the code omitted for brevity\r\n}\r\n<\/pre>\n<p>Now, when we run the app and hit one of the endpoints in <i>ValuesController<\/i> I was expecting to get an exception, as I didn&#8217;t register <i>IOptions&lt;NotificationOptions&gt;<\/i>. Surprisingly for me, the application was running normally and <i>IOptions&lt;NotificationOptions&gt;<\/i> was created with the new instance of <i>NotificationOptions<\/i> class(with all the properties defaulted).<br \/>\n<a href=\"\/\/tpodolak.com\/blog\/wp-content\/uploads\/2018\/01\/asp-net-core-make-sure-your-ioptions-is-initialized\/NotInitialized.png\"><img decoding=\"async\" src=\"\/\/tpodolak.com\/blog\/wp-content\/uploads\/2018\/01\/asp-net-core-make-sure-your-ioptions-is-initialized\/NotInitialized.png\" alt=\"\" width=\"1085\" class=\"aligncenter size-full wp-image-1315\" srcset=\"https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2018\/01\/asp-net-core-make-sure-your-ioptions-is-initialized\/NotInitialized.png 1085w, https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2018\/01\/asp-net-core-make-sure-your-ioptions-is-initialized\/NotInitialized-150x28.png 150w, https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2018\/01\/asp-net-core-make-sure-your-ioptions-is-initialized\/NotInitialized-300x55.png 300w, https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2018\/01\/asp-net-core-make-sure-your-ioptions-is-initialized\/NotInitialized-1024x189.png 1024w\" sizes=\"(max-width: 1085px) 100vw, 1085px\" \/><\/a><br \/>\nIn order to find out why it behaves like that we have to take a look how services responsible for handling <i>IOptions&lt;T&gt;<\/i> are registered in the container.<\/p>\n<pre lang=\"csharp\">\r\npublic static IServiceCollection AddOptions(this IServiceCollection services)\r\n{\r\n    if (services == null)\r\n        throw new ArgumentNullException(nameof (services));\r\n\r\n    services.TryAdd(ServiceDescriptor.Singleton(typeof (IOptions<>), typeof (OptionsManager<>)));\r\n    services.TryAdd(ServiceDescriptor.Scoped(typeof (IOptionsSnapshot<>), typeof (OptionsManager<>)));\r\n    services.TryAdd(ServiceDescriptor.Singleton(typeof (IOptionsMonitor<>), typeof (OptionsMonitor<>)));\r\n    services.TryAdd(ServiceDescriptor.Transient(typeof (IOptionsFactory<>), typeof (OptionsFactory<>)));\r\n    services.TryAdd(ServiceDescriptor.Singleton(typeof (IOptionsMonitorCache<>), typeof (OptionsCache<>)));\r\n    return services;\r\n}\r\n<\/pre>\n<p>As you can see <i>AddOptions<\/i> method registers open generics for (among others) <i>IOptions&lt;&gt;<\/i> which means that you don&rsquo;t have to register specific types of <i>IOptions&lt;T&gt;<\/i>, however, you are responsible for its configuration with<\/p>\n<pre lang=\"csharp\">\r\nservices.Configure<T>(Configuration.GetSection(\"Section\"));\r\n<\/pre>\n<p>I was always convinced that <i>services.Configure<\/i> registers and configure <i>IOptions&lt;T&gt;<\/i> but apparently, I was wrong.<\/p>\n<h3>3. Ensuring initialization <\/h3>\n<p>As these kind of errors are rather difficult to catch(at least in case of our application) I wanted to minimize the risk of doing the same bug again. The idea was to make sure that at least one <i>IConfigureOptions&lt;T&gt;<\/i> or <i>IPostConfigureOptions&lt;T&gt;<\/i> service is registered in the container (as these are the services which are responsible for setting the values of T class). My first approach was to replace default <i>OptionsFactory&lt;T&gt;<\/i> with a custom one<\/p>\n<pre lang=\"csharp\">\r\npublic class ValidatableOptionsFactory<TOptions> : OptionsFactory<TOptions> where TOptions : class, new()\r\n{\r\n    private static readonly string NamespacePrefix = typeof(Program).Namespace.Split('.').First();\r\n\r\n    public ValidatableOptionsFactory(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IPostConfigureOptions<TOptions>> postConfigures)\r\n            : base(setups, postConfigures)\r\n    {\r\n        if (typeof(TOptions).Namespace.StartsWith(NamespacePrefix) && setups.Any() == false && postConfigures.Any() == false)\r\n        {\r\n            throw new InvalidOperationException($\"No configuration options or post configuration options was found to configure {typeof(TOptions)}\");\r\n        }\r\n    }\r\n}\r\n\r\npublic class Startup\r\n{      \r\n    \/\/ This method gets called by the runtime. Use this method to add services to the container.\r\n    public void ConfigureServices(IServiceCollection services)\r\n    {\r\n        services.AddTransient(typeof(IOptionsFactory<>), typeof(ValidatableOptionsFactory<>));\r\n        services.AddOptions();\r\n    }\r\n}\r\n<\/pre>\n<p>This method works fine (although probably needs smarter TOptions filter)<br \/>\n<a href=\"\/\/tpodolak.com\/blog\/wp-content\/uploads\/2018\/01\/asp-net-core-make-sure-your-ioptions-is-initialized\/ValidatableOptionsFactory.png\"><img decoding=\"async\" src=\"\/\/tpodolak.com\/blog\/wp-content\/uploads\/2018\/01\/asp-net-core-make-sure-your-ioptions-is-initialized\/ValidatableOptionsFactory.png\" alt=\"\" width=\"1086\" class=\"aligncenter size-full wp-image-1317\" srcset=\"https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2018\/01\/asp-net-core-make-sure-your-ioptions-is-initialized\/ValidatableOptionsFactory.png 1086w, https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2018\/01\/asp-net-core-make-sure-your-ioptions-is-initialized\/ValidatableOptionsFactory-150x42.png 150w, https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2018\/01\/asp-net-core-make-sure-your-ioptions-is-initialized\/ValidatableOptionsFactory-300x85.png 300w, https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2018\/01\/asp-net-core-make-sure-your-ioptions-is-initialized\/ValidatableOptionsFactory-1024x289.png 1024w\" sizes=\"(max-width: 1086px) 100vw, 1086px\" \/><\/a><br \/>\nhowever I got to conclusion that there is no point to do validation in a runtime, and the integration test might be a better choice. I came up with following code (I am assuming that all configuration options are stored in the same assembly, as this is convention in my project)<\/p>\n<pre lang=\"csharp\">\r\npublic class OptionsTests\r\n{\r\n    private readonly Lazy<IWebHost> _webHost = new Lazy<IWebHost>(Program.BuildWebHost(null));\r\n\r\n    \/\/ this test will fail as not every option in assembly is configured\r\n    [Fact]\r\n    public void AllConfigurationOptions_HasConfigurationServicesDefined()\r\n    {\r\n        var optionsType = typeof(IOptions<>);\r\n        var constructorParameterTypes = typeof(Program).Assembly.GetExportedTypes()\r\n                .SelectMany(type => type.GetConstructors().SelectMany(ctor => ctor.GetParameters()))\r\n                .Select(parameter => parameter.ParameterType);\r\n\r\n        var optionValueTypes = constructorParameterTypes.Where(parameterType => IsAssignableToGenericType(parameterType, optionsType))\r\n                .Select(options => options.GetGenericArguments().Single())\r\n                .Distinct();\r\n            \r\n        var postConfigureOptionsType = typeof(IPostConfigureOptions<>);\r\n        var configureOptionsType = typeof(IConfigureOptions<>);\r\n\r\n        var postConfiguration = optionValueTypes.Select(type => new\r\n        {\r\n            optionsType = type,\r\n            configureOptionsType = _webHost.Value.Services.GetServices(configureOptionsType.MakeGenericType(type)),\r\n            postConfigureServices = _webHost.Value.Services.GetServices(postConfigureOptionsType.MakeGenericType(type))\r\n        });\r\n\r\n        var emptyConfigurations =\r\n                postConfiguration.Where(item => item.configureOptionsType.Any() == false && item.postConfigureServices.Any() == false)\r\n                    .Select(item => item.optionsType);\r\n\r\n        emptyConfigurations.Should().BeEmpty(\"All options should be configured\");\r\n    }\r\n\r\n    \/\/ https:\/\/stackoverflow.com\/questions\/74616\/how-to-detect-if-type-is-another-generic-type\/1075059#1075059\r\n    public static bool IsAssignableToGenericType(Type givenType, Type genericType)\r\n    {\r\n        var interfaceTypes = givenType.GetInterfaces();\r\n\r\n        if (interfaceTypes.Any(it => it.IsGenericType && it.GetGenericTypeDefinition() == genericType))\r\n        {\r\n            return true;\r\n        }\r\n\r\n        if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)\r\n            return true;\r\n\r\n        var baseType = givenType.BaseType;\r\n        if (baseType == null) return false;\r\n\r\n        return IsAssignableToGenericType(baseType, genericType);\r\n    }\r\n}\r\n<\/pre>\n<p>Running the test shows that <i>NotificationOptions<\/i> was not configured<br \/>\n<a href=\"\/\/tpodolak.com\/blog\/wp-content\/uploads\/2018\/01\/asp-net-core-make-sure-your-ioptions-is-initialized\/Test.png\"><img decoding=\"async\" src=\"\/\/tpodolak.com\/blog\/wp-content\/uploads\/2018\/01\/asp-net-core-make-sure-your-ioptions-is-initialized\/Test.png\" alt=\"\" width=\"1755\" class=\"aligncenter size-full wp-image-1316\" srcset=\"https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2018\/01\/asp-net-core-make-sure-your-ioptions-is-initialized\/Test.png 1755w, https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2018\/01\/asp-net-core-make-sure-your-ioptions-is-initialized\/Test-150x25.png 150w, https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2018\/01\/asp-net-core-make-sure-your-ioptions-is-initialized\/Test-300x50.png 300w, https:\/\/tpodolak.com\/blog\/wp-content\/uploads\/2018\/01\/asp-net-core-make-sure-your-ioptions-is-initialized\/Test-1024x172.png 1024w\" sizes=\"(max-width: 1755px) 100vw, 1755px\" \/><\/a><br \/>\nSource code for this post can be found <a href=\"https:\/\/github.com\/tpodolak\/Blog\/tree\/master\/AspNetCoreMakeSureYouInitializedYourIOptionsOfT\">here<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>1. Introduction As most people know IOptions&lt;T&gt; is a convenient way of handling configuration options in your application. Even though I&#8217;ve been using it for quite some time, last week I was unpleasantly surprised by a production bug caused by wrong usage of this mechanism. 2. Problem Let&#8217;s say we have a simple controller which [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[317],"tags":[318],"class_list":["post-1313","post","type-post","status-publish","format-standard","hentry","category-asp-net-core","tag-asp-net-core"],"_links":{"self":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/1313","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/comments?post=1313"}],"version-history":[{"count":17,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/1313\/revisions"}],"predecessor-version":[{"id":1332,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/1313\/revisions\/1332"}],"wp:attachment":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/media?parent=1313"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/categories?post=1313"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/tags?post=1313"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}