Adding extension methods to IServiceCollection in ASP.NET Core

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
4
down vote

favorite
1












I have the following extension method to add themes support to my application:



public static void AddThemes(this IServiceCollection services, Action<ThemesOptions> setup) 
services.Configure(setup);



Where ThemesOptions is defined as:



public class ThemesOptions 
public IEnumerable<string> Themes get; set;



Now in my application's startup ConfigureServices method I can say:



services.AddThemes(options => 
options.Themes = Configuration.GetSection("Themes").Get<ThemesOptions>().Themes;
);


I'm not sure that I like that I have to set every property for the options. Alternatively I tried:



services.AddThemes(options => 
options = Configuration.GetSection("Themes").Get<ThemesOptions>();
);


And:



services.AddThemes(options => Configuration.GetSection("Themes"));


However when I inject IOptions<ThemesOptions> the Themes property is null.



An alternative to I changed my extension method to:



public static void AddThemes(this IServiceCollection services, IConfiguration configuration) 
services.Configure<ThemesOptions>(configuration.GetSection("Themes"));



Now I can say the following within my application's Startup ConfigureServices method:



services.AddThemes(Configuration);


This worked fine, however, I feel the problem with this approach is that the extension method only allows the options to be set from the configuration.



I'd appreciate it if someone could confirm whether my first solution is correct and if it can be improved upon.







share|improve this question



























    up vote
    4
    down vote

    favorite
    1












    I have the following extension method to add themes support to my application:



    public static void AddThemes(this IServiceCollection services, Action<ThemesOptions> setup) 
    services.Configure(setup);



    Where ThemesOptions is defined as:



    public class ThemesOptions 
    public IEnumerable<string> Themes get; set;



    Now in my application's startup ConfigureServices method I can say:



    services.AddThemes(options => 
    options.Themes = Configuration.GetSection("Themes").Get<ThemesOptions>().Themes;
    );


    I'm not sure that I like that I have to set every property for the options. Alternatively I tried:



    services.AddThemes(options => 
    options = Configuration.GetSection("Themes").Get<ThemesOptions>();
    );


    And:



    services.AddThemes(options => Configuration.GetSection("Themes"));


    However when I inject IOptions<ThemesOptions> the Themes property is null.



    An alternative to I changed my extension method to:



    public static void AddThemes(this IServiceCollection services, IConfiguration configuration) 
    services.Configure<ThemesOptions>(configuration.GetSection("Themes"));



    Now I can say the following within my application's Startup ConfigureServices method:



    services.AddThemes(Configuration);


    This worked fine, however, I feel the problem with this approach is that the extension method only allows the options to be set from the configuration.



    I'd appreciate it if someone could confirm whether my first solution is correct and if it can be improved upon.







    share|improve this question























      up vote
      4
      down vote

      favorite
      1









      up vote
      4
      down vote

      favorite
      1






      1





      I have the following extension method to add themes support to my application:



      public static void AddThemes(this IServiceCollection services, Action<ThemesOptions> setup) 
      services.Configure(setup);



      Where ThemesOptions is defined as:



      public class ThemesOptions 
      public IEnumerable<string> Themes get; set;



      Now in my application's startup ConfigureServices method I can say:



      services.AddThemes(options => 
      options.Themes = Configuration.GetSection("Themes").Get<ThemesOptions>().Themes;
      );


      I'm not sure that I like that I have to set every property for the options. Alternatively I tried:



      services.AddThemes(options => 
      options = Configuration.GetSection("Themes").Get<ThemesOptions>();
      );


      And:



      services.AddThemes(options => Configuration.GetSection("Themes"));


      However when I inject IOptions<ThemesOptions> the Themes property is null.



      An alternative to I changed my extension method to:



      public static void AddThemes(this IServiceCollection services, IConfiguration configuration) 
      services.Configure<ThemesOptions>(configuration.GetSection("Themes"));



      Now I can say the following within my application's Startup ConfigureServices method:



      services.AddThemes(Configuration);


      This worked fine, however, I feel the problem with this approach is that the extension method only allows the options to be set from the configuration.



      I'd appreciate it if someone could confirm whether my first solution is correct and if it can be improved upon.







      share|improve this question













      I have the following extension method to add themes support to my application:



      public static void AddThemes(this IServiceCollection services, Action<ThemesOptions> setup) 
      services.Configure(setup);



      Where ThemesOptions is defined as:



      public class ThemesOptions 
      public IEnumerable<string> Themes get; set;



      Now in my application's startup ConfigureServices method I can say:



      services.AddThemes(options => 
      options.Themes = Configuration.GetSection("Themes").Get<ThemesOptions>().Themes;
      );


      I'm not sure that I like that I have to set every property for the options. Alternatively I tried:



      services.AddThemes(options => 
      options = Configuration.GetSection("Themes").Get<ThemesOptions>();
      );


      And:



      services.AddThemes(options => Configuration.GetSection("Themes"));


      However when I inject IOptions<ThemesOptions> the Themes property is null.



      An alternative to I changed my extension method to:



      public static void AddThemes(this IServiceCollection services, IConfiguration configuration) 
      services.Configure<ThemesOptions>(configuration.GetSection("Themes"));



      Now I can say the following within my application's Startup ConfigureServices method:



      services.AddThemes(Configuration);


      This worked fine, however, I feel the problem with this approach is that the extension method only allows the options to be set from the configuration.



      I'd appreciate it if someone could confirm whether my first solution is correct and if it can be improved upon.









      share|improve this question












      share|improve this question




      share|improve this question








      edited Jan 22 at 14:23









      Jamal♦

      30.1k11114225




      30.1k11114225









      asked Jan 17 at 11:24









      nfplee

      766




      766




















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          1
          down vote



          accepted










          With your ThemesOptions defined as:



          public class ThemesOptions 
          public IEnumerable<string> Themes get; set;



          and Reference #1 Configure simple options with a delegate



          public static void AddThemes(this IServiceCollection services, Action<ThemesOptions> configureOptions) 
          //Options bound and configured by a delegate
          services.Configure<ThemesOptions>(configureOptions);



          Will allow the simple delegate to be used when configuring options services.



          //Options bound and configured by a delegate
          services.AddThemes(option =>
          option.Themes = new "Theme1", "Theme2" /*, "SomeOtherTheme" */;
          );


          Now assuming an app settings json file like




          "option1": "value1_from_json",
          "option2": -1,
          "Themes": [
          "Theme1", "Theme2"
          ]



          and Reference #2 Suboptions configuration



          you defined the following



          public static void AddThemes(this IServiceCollection services, IConfiguration configuration) 
          // Bind options using a sub-section of the appsettings.json file.
          services.Configure<ThemesOptions>(configuration.GetSection("Themes"));



          and called like



          services.AddThemes(Configuration);


          As you rightly stated, it works. Yes. That is because the ThemesOptions class defines a property Themes that holds a collection of strings which would match what GetSection("Themes") would return and thus bind.



          Now let's look at the ones that did not work and why they don't



          In both cases



          services.AddThemes(options => 
          options = Configuration.GetSection("Themes").Get<ThemesOptions>();
          );


          And:



          services.AddThemes(options => Configuration.GetSection("Themes"));


          you are trying to set the value of a provided argument to the delegate which change the value of the object and in the second one, nothing is done with the option provided. When using the delegate option the value from the settings file are overridden by the configured delegate which is why Themes property of injected IOptions<ThemesOptions> is null



          services.AddThemes(options => 
          options.Themes = Configuration.GetSection("Themes").Get<ThemesOptions>().Themes;
          );


          works because you are populating the members of the passed parameter which was initialized by the options builder to the delegate.




          If this configuration section has a value, that will be used. Otherwise binding by matching property names against configuration keys recursively



          Each call to Configure<TOptions> adds an IConfigureOptions<TOptions> service to the service container. When more than one configuration service is enabled, the last configuration source specified wins and sets the configuration value.







          share|improve this answer





















          • Is there no way to set the whole configuration without setting each property individually?
            – nfplee
            Jan 24 at 21:47










          • @nfplee clarify what you mean by the whole configuration? are you talking about the one class Theme or all other options in the settings file? .
            – Nkosi
            Jan 24 at 21:51










          • I'm using this as an example of a pattern. If in the example above ThemeOptions contained more than one property I'd like to be able to set every property in one line (ideally without using reflection). I had hoped that this was a common scenario.
            – nfplee
            Jan 25 at 11:57






          • 1




            @nfplee if you look at the examples in the links provided you will see that is the convention used by core for options. If themes has multiple properties, the sub options route would populate all the matching properties on the options object
            – Nkosi
            Jan 25 at 11:59











          Your Answer




          StackExchange.ifUsing("editor", function ()
          return StackExchange.using("mathjaxEditing", function ()
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          );
          );
          , "mathjax-editing");

          StackExchange.ifUsing("editor", function ()
          StackExchange.using("externalEditor", function ()
          StackExchange.using("snippets", function ()
          StackExchange.snippets.init();
          );
          );
          , "code-snippets");

          StackExchange.ready(function()
          var channelOptions =
          tags: "".split(" "),
          id: "196"
          ;
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function()
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled)
          StackExchange.using("snippets", function()
          createEditor();
          );

          else
          createEditor();

          );

          function createEditor()
          StackExchange.prepareEditor(
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: false,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );








           

          draft saved


          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f185297%2fadding-extension-methods-to-iservicecollection-in-asp-net-core%23new-answer', 'question_page');

          );

          Post as a guest






























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          1
          down vote



          accepted










          With your ThemesOptions defined as:



          public class ThemesOptions 
          public IEnumerable<string> Themes get; set;



          and Reference #1 Configure simple options with a delegate



          public static void AddThemes(this IServiceCollection services, Action<ThemesOptions> configureOptions) 
          //Options bound and configured by a delegate
          services.Configure<ThemesOptions>(configureOptions);



          Will allow the simple delegate to be used when configuring options services.



          //Options bound and configured by a delegate
          services.AddThemes(option =>
          option.Themes = new "Theme1", "Theme2" /*, "SomeOtherTheme" */;
          );


          Now assuming an app settings json file like




          "option1": "value1_from_json",
          "option2": -1,
          "Themes": [
          "Theme1", "Theme2"
          ]



          and Reference #2 Suboptions configuration



          you defined the following



          public static void AddThemes(this IServiceCollection services, IConfiguration configuration) 
          // Bind options using a sub-section of the appsettings.json file.
          services.Configure<ThemesOptions>(configuration.GetSection("Themes"));



          and called like



          services.AddThemes(Configuration);


          As you rightly stated, it works. Yes. That is because the ThemesOptions class defines a property Themes that holds a collection of strings which would match what GetSection("Themes") would return and thus bind.



          Now let's look at the ones that did not work and why they don't



          In both cases



          services.AddThemes(options => 
          options = Configuration.GetSection("Themes").Get<ThemesOptions>();
          );


          And:



          services.AddThemes(options => Configuration.GetSection("Themes"));


          you are trying to set the value of a provided argument to the delegate which change the value of the object and in the second one, nothing is done with the option provided. When using the delegate option the value from the settings file are overridden by the configured delegate which is why Themes property of injected IOptions<ThemesOptions> is null



          services.AddThemes(options => 
          options.Themes = Configuration.GetSection("Themes").Get<ThemesOptions>().Themes;
          );


          works because you are populating the members of the passed parameter which was initialized by the options builder to the delegate.




          If this configuration section has a value, that will be used. Otherwise binding by matching property names against configuration keys recursively



          Each call to Configure<TOptions> adds an IConfigureOptions<TOptions> service to the service container. When more than one configuration service is enabled, the last configuration source specified wins and sets the configuration value.







          share|improve this answer





















          • Is there no way to set the whole configuration without setting each property individually?
            – nfplee
            Jan 24 at 21:47










          • @nfplee clarify what you mean by the whole configuration? are you talking about the one class Theme or all other options in the settings file? .
            – Nkosi
            Jan 24 at 21:51










          • I'm using this as an example of a pattern. If in the example above ThemeOptions contained more than one property I'd like to be able to set every property in one line (ideally without using reflection). I had hoped that this was a common scenario.
            – nfplee
            Jan 25 at 11:57






          • 1




            @nfplee if you look at the examples in the links provided you will see that is the convention used by core for options. If themes has multiple properties, the sub options route would populate all the matching properties on the options object
            – Nkosi
            Jan 25 at 11:59















          up vote
          1
          down vote



          accepted










          With your ThemesOptions defined as:



          public class ThemesOptions 
          public IEnumerable<string> Themes get; set;



          and Reference #1 Configure simple options with a delegate



          public static void AddThemes(this IServiceCollection services, Action<ThemesOptions> configureOptions) 
          //Options bound and configured by a delegate
          services.Configure<ThemesOptions>(configureOptions);



          Will allow the simple delegate to be used when configuring options services.



          //Options bound and configured by a delegate
          services.AddThemes(option =>
          option.Themes = new "Theme1", "Theme2" /*, "SomeOtherTheme" */;
          );


          Now assuming an app settings json file like




          "option1": "value1_from_json",
          "option2": -1,
          "Themes": [
          "Theme1", "Theme2"
          ]



          and Reference #2 Suboptions configuration



          you defined the following



          public static void AddThemes(this IServiceCollection services, IConfiguration configuration) 
          // Bind options using a sub-section of the appsettings.json file.
          services.Configure<ThemesOptions>(configuration.GetSection("Themes"));



          and called like



          services.AddThemes(Configuration);


          As you rightly stated, it works. Yes. That is because the ThemesOptions class defines a property Themes that holds a collection of strings which would match what GetSection("Themes") would return and thus bind.



          Now let's look at the ones that did not work and why they don't



          In both cases



          services.AddThemes(options => 
          options = Configuration.GetSection("Themes").Get<ThemesOptions>();
          );


          And:



          services.AddThemes(options => Configuration.GetSection("Themes"));


          you are trying to set the value of a provided argument to the delegate which change the value of the object and in the second one, nothing is done with the option provided. When using the delegate option the value from the settings file are overridden by the configured delegate which is why Themes property of injected IOptions<ThemesOptions> is null



          services.AddThemes(options => 
          options.Themes = Configuration.GetSection("Themes").Get<ThemesOptions>().Themes;
          );


          works because you are populating the members of the passed parameter which was initialized by the options builder to the delegate.




          If this configuration section has a value, that will be used. Otherwise binding by matching property names against configuration keys recursively



          Each call to Configure<TOptions> adds an IConfigureOptions<TOptions> service to the service container. When more than one configuration service is enabled, the last configuration source specified wins and sets the configuration value.







          share|improve this answer





















          • Is there no way to set the whole configuration without setting each property individually?
            – nfplee
            Jan 24 at 21:47










          • @nfplee clarify what you mean by the whole configuration? are you talking about the one class Theme or all other options in the settings file? .
            – Nkosi
            Jan 24 at 21:51










          • I'm using this as an example of a pattern. If in the example above ThemeOptions contained more than one property I'd like to be able to set every property in one line (ideally without using reflection). I had hoped that this was a common scenario.
            – nfplee
            Jan 25 at 11:57






          • 1




            @nfplee if you look at the examples in the links provided you will see that is the convention used by core for options. If themes has multiple properties, the sub options route would populate all the matching properties on the options object
            – Nkosi
            Jan 25 at 11:59













          up vote
          1
          down vote



          accepted







          up vote
          1
          down vote



          accepted






          With your ThemesOptions defined as:



          public class ThemesOptions 
          public IEnumerable<string> Themes get; set;



          and Reference #1 Configure simple options with a delegate



          public static void AddThemes(this IServiceCollection services, Action<ThemesOptions> configureOptions) 
          //Options bound and configured by a delegate
          services.Configure<ThemesOptions>(configureOptions);



          Will allow the simple delegate to be used when configuring options services.



          //Options bound and configured by a delegate
          services.AddThemes(option =>
          option.Themes = new "Theme1", "Theme2" /*, "SomeOtherTheme" */;
          );


          Now assuming an app settings json file like




          "option1": "value1_from_json",
          "option2": -1,
          "Themes": [
          "Theme1", "Theme2"
          ]



          and Reference #2 Suboptions configuration



          you defined the following



          public static void AddThemes(this IServiceCollection services, IConfiguration configuration) 
          // Bind options using a sub-section of the appsettings.json file.
          services.Configure<ThemesOptions>(configuration.GetSection("Themes"));



          and called like



          services.AddThemes(Configuration);


          As you rightly stated, it works. Yes. That is because the ThemesOptions class defines a property Themes that holds a collection of strings which would match what GetSection("Themes") would return and thus bind.



          Now let's look at the ones that did not work and why they don't



          In both cases



          services.AddThemes(options => 
          options = Configuration.GetSection("Themes").Get<ThemesOptions>();
          );


          And:



          services.AddThemes(options => Configuration.GetSection("Themes"));


          you are trying to set the value of a provided argument to the delegate which change the value of the object and in the second one, nothing is done with the option provided. When using the delegate option the value from the settings file are overridden by the configured delegate which is why Themes property of injected IOptions<ThemesOptions> is null



          services.AddThemes(options => 
          options.Themes = Configuration.GetSection("Themes").Get<ThemesOptions>().Themes;
          );


          works because you are populating the members of the passed parameter which was initialized by the options builder to the delegate.




          If this configuration section has a value, that will be used. Otherwise binding by matching property names against configuration keys recursively



          Each call to Configure<TOptions> adds an IConfigureOptions<TOptions> service to the service container. When more than one configuration service is enabled, the last configuration source specified wins and sets the configuration value.







          share|improve this answer













          With your ThemesOptions defined as:



          public class ThemesOptions 
          public IEnumerable<string> Themes get; set;



          and Reference #1 Configure simple options with a delegate



          public static void AddThemes(this IServiceCollection services, Action<ThemesOptions> configureOptions) 
          //Options bound and configured by a delegate
          services.Configure<ThemesOptions>(configureOptions);



          Will allow the simple delegate to be used when configuring options services.



          //Options bound and configured by a delegate
          services.AddThemes(option =>
          option.Themes = new "Theme1", "Theme2" /*, "SomeOtherTheme" */;
          );


          Now assuming an app settings json file like




          "option1": "value1_from_json",
          "option2": -1,
          "Themes": [
          "Theme1", "Theme2"
          ]



          and Reference #2 Suboptions configuration



          you defined the following



          public static void AddThemes(this IServiceCollection services, IConfiguration configuration) 
          // Bind options using a sub-section of the appsettings.json file.
          services.Configure<ThemesOptions>(configuration.GetSection("Themes"));



          and called like



          services.AddThemes(Configuration);


          As you rightly stated, it works. Yes. That is because the ThemesOptions class defines a property Themes that holds a collection of strings which would match what GetSection("Themes") would return and thus bind.



          Now let's look at the ones that did not work and why they don't



          In both cases



          services.AddThemes(options => 
          options = Configuration.GetSection("Themes").Get<ThemesOptions>();
          );


          And:



          services.AddThemes(options => Configuration.GetSection("Themes"));


          you are trying to set the value of a provided argument to the delegate which change the value of the object and in the second one, nothing is done with the option provided. When using the delegate option the value from the settings file are overridden by the configured delegate which is why Themes property of injected IOptions<ThemesOptions> is null



          services.AddThemes(options => 
          options.Themes = Configuration.GetSection("Themes").Get<ThemesOptions>().Themes;
          );


          works because you are populating the members of the passed parameter which was initialized by the options builder to the delegate.




          If this configuration section has a value, that will be used. Otherwise binding by matching property names against configuration keys recursively



          Each call to Configure<TOptions> adds an IConfigureOptions<TOptions> service to the service container. When more than one configuration service is enabled, the last configuration source specified wins and sets the configuration value.








          share|improve this answer













          share|improve this answer



          share|improve this answer











          answered Jan 22 at 23:53









          Nkosi

          1,870619




          1,870619











          • Is there no way to set the whole configuration without setting each property individually?
            – nfplee
            Jan 24 at 21:47










          • @nfplee clarify what you mean by the whole configuration? are you talking about the one class Theme or all other options in the settings file? .
            – Nkosi
            Jan 24 at 21:51










          • I'm using this as an example of a pattern. If in the example above ThemeOptions contained more than one property I'd like to be able to set every property in one line (ideally without using reflection). I had hoped that this was a common scenario.
            – nfplee
            Jan 25 at 11:57






          • 1




            @nfplee if you look at the examples in the links provided you will see that is the convention used by core for options. If themes has multiple properties, the sub options route would populate all the matching properties on the options object
            – Nkosi
            Jan 25 at 11:59

















          • Is there no way to set the whole configuration without setting each property individually?
            – nfplee
            Jan 24 at 21:47










          • @nfplee clarify what you mean by the whole configuration? are you talking about the one class Theme or all other options in the settings file? .
            – Nkosi
            Jan 24 at 21:51










          • I'm using this as an example of a pattern. If in the example above ThemeOptions contained more than one property I'd like to be able to set every property in one line (ideally without using reflection). I had hoped that this was a common scenario.
            – nfplee
            Jan 25 at 11:57






          • 1




            @nfplee if you look at the examples in the links provided you will see that is the convention used by core for options. If themes has multiple properties, the sub options route would populate all the matching properties on the options object
            – Nkosi
            Jan 25 at 11:59
















          Is there no way to set the whole configuration without setting each property individually?
          – nfplee
          Jan 24 at 21:47




          Is there no way to set the whole configuration without setting each property individually?
          – nfplee
          Jan 24 at 21:47












          @nfplee clarify what you mean by the whole configuration? are you talking about the one class Theme or all other options in the settings file? .
          – Nkosi
          Jan 24 at 21:51




          @nfplee clarify what you mean by the whole configuration? are you talking about the one class Theme or all other options in the settings file? .
          – Nkosi
          Jan 24 at 21:51












          I'm using this as an example of a pattern. If in the example above ThemeOptions contained more than one property I'd like to be able to set every property in one line (ideally without using reflection). I had hoped that this was a common scenario.
          – nfplee
          Jan 25 at 11:57




          I'm using this as an example of a pattern. If in the example above ThemeOptions contained more than one property I'd like to be able to set every property in one line (ideally without using reflection). I had hoped that this was a common scenario.
          – nfplee
          Jan 25 at 11:57




          1




          1




          @nfplee if you look at the examples in the links provided you will see that is the convention used by core for options. If themes has multiple properties, the sub options route would populate all the matching properties on the options object
          – Nkosi
          Jan 25 at 11:59





          @nfplee if you look at the examples in the links provided you will see that is the convention used by core for options. If themes has multiple properties, the sub options route would populate all the matching properties on the options object
          – Nkosi
          Jan 25 at 11:59













           

          draft saved


          draft discarded


























           


          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f185297%2fadding-extension-methods-to-iservicecollection-in-asp-net-core%23new-answer', 'question_page');

          );

          Post as a guest













































































          Popular posts from this blog

          Chat program with C++ and SFML

          Function to Return a JSON Like Objects Using VBA Collections and Arrays

          Will my employers contract hold up in court?