GithubHelp home page GithubHelp logo

aspnet / options Goto Github PK

View Code? Open in Web Editor NEW
150.0 50.0 69.0 539 KB

[Archived] A framework for accessing and configuring POCO settings. Project moved to https://github.com/aspnet/Extensions

License: Apache License 2.0

Shell 4.40% C# 92.19% Batchfile 0.24% PowerShell 3.16%
aspnet-product

options's Introduction

Options [Archived]

This GitHub project has been archived. Ongoing development on this project can be found in https://github.com/aspnet/Extensions.

Options is a framework for accessing and configuring POCO settings.

This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the AspNetCore repo.

options's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

options's Issues

NeutralResourcesLanguage Warning on UWP

I'm getting the following warning on a Universal Windows App.

warning MSB3817: The assembly "Microsoft.Framework.OptionsModel.dll" does not have a NeutralResourcesLanguageAttribute on it. To be used in an app package, portable libraries must define a NeutralResourcesLanguageAttribute on their main assembly (ie, the one containing code, not a satellite assembly).

How to get settings using the configuration API?

I'm having problems with the configuration API.

I have some keys on user-secrets like this:

user-secret list
info: Authentication:Facebook:ApplicationId = 427****
info: Authentication:Facebook:ApplicationSecret = 132***

appsettings.json

  "Authentication": {
    "Facebook": {
      "ApplicationId": "secret",
      "ApplicationSecret": "secret"
    }
  }

in startup class:

var builder = new ConfigurationBuilder()
                .SetBasePath(appEnv.ApplicationBasePath)
                .AddJsonFile("appsettings.json")
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

            if (env.IsDevelopment()) {
                // This reads the configuration keys from the secret store.
                // For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
                builder.AddUserSecrets();
            }
            builder.AddEnvironmentVariables();
            Configuration = builder.Build();

and my class

public class FacebookAuthentication
{
    public string ApplicationId { private get; set; }
    public string ApplicationSecret { private get; set; }
}

I would love to set those settings on DI and access them in Configure or anywhere in my application.
I read that I need to inject IOptions<FacebookAuthentication> instead of FacebookAuthentication, but for now, i just only want to get those values somehow, nothing seems to work as I expect them to.

Note: The strongly typed object I want is not at the root of the configuration.

This is what I tried:

services.Configure<FacebookAuthentication>(Configuration.GetSection("Authentication:Facebook"));
services.Configure<FacebookAuthentication>(Configuration.GetSection("Authentication").GetSection("Facebook"));

------
var facebookConfiguration1 = app.ApplicationServices.GetRequiredService<IOptions<FacebookAuthentication>>();
var facebookConfiguration2 = Configuration.Get<FacebookAuthentication>("Authentication:Facebook");
var facebookConfiguration3 = Configuration.GetSection("Authentication:Facebook");

none of this seems to work. Can someone point me out how should I get those settings (if it's possible) or I should move the Strongly Type to the root or what I'm doing wrong please.

As bonus feedback in case you are interested: I found the api to be cumbersome, not discoverable and the need to inject IOption<T> very annoying.

Options do not work on .NET Core in ASP.NET 5 Web template

  • Create ASP.NET Website template
  • Switch to Core CLR
  • Run it

The application name is not being set. SiteTitle is being read from config using options model.
In startup we have

services.Configure<AppSettings>(Configuration.GetSubKey("AppSettings"));

In _Layout we have

@inject IOptions<AppSettings> AppSettings
    <title>@ViewBag.Title - @AppSettings.Options.SiteTitle</title>

Options from configuration usage in frameworks service sugar methods is incorrect

When a framework such as SignalR adds its default services to the service collection:

services.AddSignalR(configuration);

It should read by-convention from configuration something like this:

"SignalROptions": {
        "EnableJSONP": "true"
}

Instead it is assuming that the configuration object passed contains EnableJSONP as a top level key. In other words, most frameworks are missing a call to configuration.GetSubKey(frameworkName) inside their Add[Framework] sugar methods.

Syntax sugar for Func/Delegate/Type

Syntax sugar would be specifically to have a value-type that can be used as options.Notification to hold the delegate, and also to hold onto the delegate.

@inject does not work - all fields are always null.

appsettings.cs:

   public class AppSettings
    {
        public string SiteTitle { get; set; }
    }

appsettings.json:

{
    "AppSettings": {
    "SiteTitle": "ZaxiTech"
  }

Startup.cs:

public static IConfiguration Configuration { get; set; }

services.AddOptions();  // Setup options with DI
services.Configure<AppSettings>(Configuration);

xview.cshtml:

@using Microsoft.Extensions.OptionsModel
@inject IOptions<AppSettings> MyAppSettings

And all fields like SiteTitle in MyAppSettings are always null.

Review usage of options in middleware

Should middleware usage of options be consistent? Auth middlewares are using IOptions, while many others, Diagnostics, Routing, StaticFiles etc are still using instance based options.

Try Pipeline options

See if we can move the middleware usings into a new options class,

IPipelineOptions which has an ordered list of Middlewares, then the pipeline can be constructed from DI using IPipelineOptions without the explicit seperate Using blocks.

Consider adding the Action-property to the IConfigureOptions-interface.

At the current state, the IConfigureOptions-interface does not contain the Action-property.
But the ConfigureOptions-class does.

Since the Configure<T>-extension adds the options as a IConfigureOptions<T> to the service-collection, casts have to be made in order to utilize the Action-property, which is needed to properly implement the options.

Adding the property to the IConfigureOptions-interface would make for smoother implementations.

Here is an example of what i mean:
https://github.com/mausworks/MausWorks.MongoDB-Service/blob/master/MongoDBContextProvider.cs#L38,L53

Called from:
https://github.com/rausmaus/MausWorks.MongoDB-Service/blob/master/DependencyInjection/MongoDBServiceCollectionExtensions.cs#L83-L88

I might be wrong in my approach of doing this; but EntityFramrowk does not use the OptionsModel and it kind of does it the same way.

Options binding from IConfiguration doesn't work with Nullable<T>

Like the title says ๐Ÿ˜„

Repro:

[Fact]
public void Configure_GetsNullableOptionsFromConfiguration()
{
    // Arrange
    var configValues = new Dictionary<string, string>
    {
        { "MyBool", "true" },
        { "MyNullableBool", "true" }
    };
    var config = new Configuration(new MemoryConfigurationSource(configValues));
    var services = new ServiceCollection();
    services.Configure<TestOptions>(config);
    var serviceProvider = services.BuildServiceProvider();

    // Act
    var testOptions = new TestOptions();
    serviceProvider.GetService<IConfigureOptions<TestOptions>>()
        .Configure(testOptions);

    // Assert
    Assert.True(testOptions.MyBool);
    /* FAILS -> */ Assert.NotNull(testOptions.MyNullableBool);
    Assert.True(testOptions.MyNullableBool.Value);
}

public class TestOptions
{
    public bool MyBool { get; set; }
    public string MyString { get; set; }
    public bool? MyNullableBool { get; set; }
}

Revisit Options Order

1.The default implementation for option accessor and option setups seems to prevent scoped option setups from working. I believe these more dynamic scenarios are important and not necessarily exclusive to data.
2.I think it would be good to have a nice pattern to register option setups that override or not override other option setups, i.e. the issue of specifying the "Order" of the IOptionsSetup. Hao proposed using a flag, which I am ok with trying. Not sure it will make it to this checking though.
3.The implementation of binding to keys from configuration to matching properties on TOptions only work with core and desktop .NET and I haven't got a good answer on whether this can be addressed.

BCL API review: Consider decoupling options from DI

Options was designed to work well with DI, but the only part that really depends directly on DI is the extension methods on IServiceCollection: Configure() and AddOptions(). The idea with this issue is propose we separate those extension methods into a different package from the core of Options which in theory could be used independently of DI, e.g. using code generation.

Review all middleware options usage

I think we are pretty close to having the consistent pattern for options usage in middlewares from the discussion last week:

  1. All middleware should take IOptions<TOptions> instead of directly taking an instance of TOptions

  2. UseMiddleware should have the follow two overloads:

    UseXyz(options => options.Whatever = "yar") // use shared options from DI, and apply action to tweak
    UseXyz(new TOptions() { Whaterver = "yar" }) // forget about what's in DI, just use this instance
  3. Introduce a new abstract Options<TOptions> base class that implements IOptions<TOptions> by simply returning this, that all the framework options can dervice from to easily get this behavior for free.

I can make a pass through all the repos to make things consistent assuming we all agree that this what we want...

Thoughts @divega @lodejard @davidfowl ?

Add extension methods for reloadable options

We want to add a set of extension methods for linking common patterns of use for reloadable options:

public static void Subscribe(this IFileProvider fileProvider, string filename,  IConfigurationRoot configuration)
{
    ChangeToken.OnChange((f) => f.Watch(filename), () => configuration.Reload());
}

public static void Subscribe<TOptions>(this IConfigurationRoot configuration, IReloadOptionsManager reload)
{
    ChangeToken.OnChange((c) => c.GetReloadToken(), () => reload.Reload<TOptions>());
}

These may live here, or in another repo.

Consider enabling configured options types to be resolved/activated directly without using IOptions<TOptions>

Currently in order to trigger the options "pipeline" to retrieve a configured instance of TOptions it is necessary to call serviceProvider.GetService<IOptions<TOptions>>(). The capability now exists in DI to register a factory Func<IServiceProvider,T> and we could use this capability to enable serviceProvider.GetService<TOptions>() to work as well.

We should discuss whether this is a good idea (it blurs the line between services and options a bit more), and besides, there are few details that we would need to figure out:

  1. Currently a service IOptions<> can be registered as an open generic and only once. Factories cannot be registered as open generic. We would need to figure out when it is the right time to register the factory. Possibly Configure<TOptions>() is the right place to do it, but it would potentially be registered multiple times, although that might be ok.
  2. Can we make named options work with this? Probably not, but we can try. GetService most likely wouldn't work, unless we added generalized named services. Even without the latter, for activation one idea is to define an OptionsNameAttribute that can be applied to the TOptions constructor parameter. TypeActivator would need to pull this name while reflecting on the constructor and we would need the ability for factories in DI to take other arguments besides the IServiceProvider.

Breakup dependencies, e.g. with Configuration

Currently Options depends on Configuration because of the serviceCollection.Configure(configuration) overload.

This means that any package that indirectly depend on Options also depend on Configuration, e.g. Entity Framework 7 depends on Caching which depends on Options which depends on Configuration, therefore even if we made the explicit decision not to couple Entity Framework with Configuration, the dependency exists and is carried even to places where it isn't commonly used, e.g. desktop .NET or Windows UAP applications.

The dependency could be broken by splitting the parts of options that depend from configuration (e.g. serviceCollection.Configure(configuration) and its implementation classConfigureFromConfigurationOptions`) into their own package.

Optionally, moving that package in its own repo would help breakup the chain of build dependencies for our CI.

CLS compliance

Is the latest version is CLS-compliant? I was not able to compile assembly marked as CLS-compliant with "Microsoft.Framework.OptionsModel": "1.0.0-beta8"

Revisit by-convention options from configuration

@Eilon has expressed some concerns with the "amount of magic" in the current design that pulls configuration information into an instance of an options type MyOptions through a call to services.Configure<MyOptions>(configuration), and on how this is currently used across the frameworks. Filing this issue to track that so that we can have a follow up conversation.

Options binding from configuration should not have conditionally compiled code

Update: Covert.ChangeType() is now available in System.Runtime.Extensions and we can remove the conditional compilation.

Currently the whole body of OptionsServices.ReadProperties(object, IConfiguration) is in an #if and won't do anything if the functionality of Convert.ChangeType() presumably isn't available in the target platform. I believe we should decide more explicitly what we want to do with this, e.g.:

  1. Throw an exception
  2. Make sure the needed functionality is made available in CoreCLR and use it

Thread safety in OptionsManager

In the current implementation of OptionsManager, if I do something like this:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<MyOptions>(options => {
          // some code, maybe not trivial
    });
}

and then later, from multiple threads:

var options = services.GetRequiredService<IOptions<MyOptions>>().Value;

the configuration action may be invoked more than once, which is not intuitively expected.

If this is the desired behavior, it needs to be documented very clearly. Personally, I would prefer the options manager to deal with concurrency issues and guarantee that the configuration action is only invoked once.

Do we need ConfigureFooOptions extensions

If we have AddIdentity(IConfigureOptions)

Discuss whether we need more than just basic Configure and the high level one that is a part of AddFramework(IConfigureOptions)

Support for partial keys

Using this framework it is possible to load an entire config file into a object. However, it appears to be all or nothing. I can't, for example, load parts of the configuration into separate objects. For example:

{
   "logging" : {  /*options for logging here*/   }
   "printing" : {  /*options for printing here*/   }
   "otherStuff" : {  /*options for xxx here*/   }
   "moreStuff" : {  /*options for yyy here*/   }
}

I suppose I could just create one container object that has these other objects within it, but might be nice to support loading these partial object directly so the DI's are all separated. Or, I guess I could create multiple log files, but that seems messy as well.

Rationale: I like each framework to be completely independent of the other frameworks (like logging). So, the log framework would get it's log settings independent of the others.

Add support for named options

        SetupNamedOption("ApplicationCookie", options => { })
        UseCookieAuthentication("ApplicationCookie")
        SetupNamedOption("ExternalCookie", options => { })
        UseCookieAuthentication("ApplicationCookie")

        // Overloads which use default/no name
        SetupOption(options => { })
        UseGoogleAuthentication()

Add Name/GetNamedOptions to IOptionsSetup/OptionsAccessor

Consider adding AddOptions() extension method to add default Options services to a service collection

Currently Options defines and provides the default implementation of IOptions<> but unlike all other components it only defines GetDefaultServices([IConfiguration]) method and not the AddOptions(this IServiceCollection, [IConfiguration]) sugar extension method. Adding the latter would improve consistency and would be useful for testing and for non-Web application scenarios that don't have Hosting (which adds OptionsServices.GetDefaultSErvices() automatically) but want to take advantage of these basic services using a friendlier API.

Make options reloadable

API changes

IReloadOptions NEW

public interface IReloadOptionsManager
{
    IChangeToken GetReloadToken<TOptions>();
    void Reload<TOptions>();
}

IReloadableOptions NEW

public interface IReloadableOptions<T>
{
    T Value { get; }
    IDisposable Subscribe(Action<T> value);
}

Design Summary

With this approach, there's a new type for consumers of options which want the ability to see updates that occur after
application startup.

Consumers can DI the IReloadableOptions<T> and see updates according to three supported patterns:

1.) Get the current value when the component was created (useful for scoped and transient business objects who don't want
to deal with concurrency issues).

public class HomeController
{
    private AppSettings _settings;

    public HomeController(IReloadableOptions<AppSettings> options)
    {
        _settings = options.Value;
    }

    private bool PriceOfGas => _settings.PriceOfGas;
}

This differs from the normal IOptions<T> behavior. Value can change after it is first accessed.

2.) Get the current value each time .Value is called. Useful when you want realtime updates, but don't have a complicated
set of concurrency behaviors to deal with.

public class HomeController
{
    private IReloadableOptions<AppSettings> _options;

    public HomeController(IReloadableOptions<AppSettings> options)
    {
        _options = options;
    }

    private bool PriceOfGas => _options.Value.PriceOfGas;
}

3.) Be notified when the value changes. Useful for long-lived components that was to recompute data when options change.

public class ProductPriceService : IDisposable
{
    private AppSettings _settings;
    private IDisposable _subscription;

    public ProductPriceService(IReloadableOptions<AppSettings> options)
    {
        _subscription = options.Subscribe(s => _settings = s);
    }

    private bool PriceOfGas => _settings.PriceOfGas;

    public void Dispose()
    {
        _subscription?.Dispose();
    }
}

When using this pattern you WILL LEAK if you don't unregister your callback. The callback is called immediately and
the surrounding object needs to be prepared to deal with that case.

Add support for binding property to IConfiguration

In omnisharp I want to have a configuration like this:

{
   "msbuild": {
      "toolsVersion": "14.0",
       "properties": {
           "RandomProperty": "1",
           "VisualStudioVersion": "14.0"
        }
   }
}

I would like to express my options like so:

public class MSBuildOptions
{
     public string ToolsVersion { get; set; }
     public IConfiguration Properties { get; set; }
}

That way I would be able to express both the latebound and strongly typed properties on the same model object.

/cc @glennc @muratg @Eilon @lodejard

API Design: make strongly typed settings easier

I am lifting some discussion that was briefly started by @brockallen in #31 into its own issue.

I have been using the IOptions API for a while now in the context of application settings and it just feels convoluted for no reason. I have the impression that I repeat meaningless, verbose, boilerplate code because... well because the framework wants me to.

Basically, anywhere a component needs to adjust its behaviour based on a setting (typically stored in config.json), you see a pattern similar to:

class FrobController
{
  private readonly AppSettings settings;

  public FrobController(IOptions<AppSettings> options)
  {
    this.settings = options.Options; // or options.Value in beta-8 I think
  }

  public void FrobMe()
  {
    for (int i = 0; i < settings.FrobbingTimes; i++)
      FrobOnce();
  }
}

And if you use settings in many places or have split your settings in several classes by domain, you repeat that ctor code quite a lot. What bothers me?

  • What the hell is this IOptions<>? Vague naming is vague. And it's not the options since they are in AppSettings, actually.
  • What is it about? Why should I use it? What does it buy me? In this use-case: nothing!
  • Given its generic name and its non-existent added value, it's hard to discover. Trust me, the first time you encounter this you try to DI AppSettings. And it fails. Then you Google.
  • The dance around .Options or .Value is superfluous.
  • The naming is awkward. What should I call the parameter IOptions? Any name I could come up with is either long for what it's worth (optionsAccessor) or misleading/clashing with the actual options object (AppSettings).

In #31 @lodejard said:

it is very similar, but the accessor service has advantages in that the creation of the singleton is deferred to when it's first used.

Deferred creation is something that needs a general solution, not a domain-specific one. And the solution is injecting Lazy<T> and registering a factory for T. And given the smallish nature of application settings in general and the fact that they are guaranteed to be used, the lazy initialization is probably more overhead than benefit.

Plus it enables the pattern where multiple IConfigureOptions can be added as services to alter any given TOptions.

Maybe for middleware configs or other advanced stuff. I don't see how that would be a use case for application settings. The use-case here is really to parse config into a strongly-typed object and make it available to the whole app.

With a singleton MyOptions added to DI it must be fully baked before it is added, and the things doing the options configuration all need to do their work before IOC is available.

Not necessarily. Alternative design can surely be considered, like having the "things" mutate the options after the fact, or registering a factory that applies the "things" just in time when creating the actual instance. Anyway I don't see how this is pertinent for app settings.

My point is that probably those things are important for other scenarios (middleware configuration?) but I think the typed application settings use case should be simplified. What feels right is injecting the typed class directly.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.