Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for a App.config settings #165

Open
apla opened this issue Jun 18, 2015 · 9 comments
Open

Support for a App.config settings #165

apla opened this issue Jun 18, 2015 · 9 comments
Labels

Comments

@apla
Copy link

apla commented Jun 18, 2015

I'm trying to couple commandline Options class with ConfigurationManager.AppSettings to make configuration persistent. For now (with stable version 1.9.x) I ended with such piece of code:

public Options (string[] args) {

    // TODO: redesign without reflections
    var pis = this.GetType().GetProperties().Where(prop => prop.IsDefined(typeof(OptionAttribute), false));
    foreach (var pi in pis) {
        var optAttr = pi.GetCustomAttributes(typeof(OptionAttribute), false).FirstOrDefault() as OptionAttribute;
        if (optAttr == null)
            continue;
        optAttr.DefaultValue = optAttr.DefaultValue ?? ConfigurationManager.AppSettings[optAttr.LongName];
    }

    // here we use CommandLine to initialize this class
    using (var parser = new CommandLine.Parser(settings => { settings.HelpWriter = Console.Error; settings.IgnoreUnknownArguments = false; }))
...

Maybe you can propose better way to enumerate properties?

@gsscoder
Copy link
Owner

@apla, code can help, but can you first describe exactly what you're trying to doing?
When talking about Options class and persistence, do you want serialize an instance in your settings or "externalize" attributes metadata?

I want to add that, if I'm not wrong, that @nemec already done a configuration component coupled with this project. You can check its repo or ask him.

@nemec
Copy link
Collaborator

nemec commented Jun 18, 2015

Hi @apla, are you asking for a way to load settings from App.config and override some of them with command line parameters? Do you also need to save command line overrides back to the App.config?

For the first one, I wrote a library called SmartConf that does just that -- set the "order" of your data sources and they will all be merged into on config. Additionally, I wrote sources for AppSettings and CommandLine (see here, the readme for CommandLine is in the CommandLine folder).

If you need to save the data back, unfortunately I haven't implemented that yet for AppSettings. You could load settings from a regular XML file (XmlFileConfigurationSource) instead, which does support saving.

@apla
Copy link
Author

apla commented Jun 18, 2015

I want to split project development to two phases. When I write code and test it I change configuration via command line, and when my requirements are satisfied, I want to store that config via persistence layer (for example, App.config). Then I can distribute my project along with working configuration. Also, using that configuration allows me to pin into windows 8.x taskbar, because all cli parameters is lost when I pin program to taskbar.

I don't need to change configuration programmatically, App.config shall provide defaults.

For example, I'm writing borderless fullscreen browser. I want to configure starting url to http://instance-a.site.com/ and use cefsharp for that browser. Another client wants to use old site with ActiveX, so I need to configure url as http://instance-b.site.com and define WebBrowser version as IE 8. When I'm ready, I just send zip archives with working configuration using only one binary.

@nemec, Do I need to provide one configuration class for App.config and another one for commandline? Or I can use one Config class like this?

private sealed class Options {
    [Option ("url", MetaValue = "URL", HelpText = "Url to show on multiscreen")]
    public string Url { get; set; }

    [Option ('e', "engine", DefaultValue ="chrome", HelpText = "Browser engine to use: ie or chrome")]
    public string Engine { get; set; }

...

var manager = new ConfigurationManager<Config>(
    new AppSettingsConfigurationSource<Config>(),
    new CommandLineConfigurationSource<Config>(args)
);
var config = manager.Out;

@nemec
Copy link
Collaborator

nemec commented Jun 18, 2015 via email

@gsscoder
Copy link
Owner

As you seen, @apla, @nemec helped you... I want to add that we're happy you choose this library and that your way of using it for automating tests is very cool.

@diev
Copy link

diev commented Aug 26, 2015

Fine, I was looking for same solution as @apla too!

@gsscoder
Copy link
Owner

@dlev, as said @nemec it own a project that can help in such direction or at least can be used to get inspiration.

This is what I'm planning for post-2.0-stable:

class Options {
  // normal options here ...omissis...

  [Option(Default="Fallback")] //Fallback special string that force the lib to look...
  public int Port { get; set; }

  internal Fallback<int> PortFallback { //...for {Property}Fallback as CoC phylosophy.
    get { retrun Fallback.AppConfig("section/value").Or(8080); }
  }
}

In few words you'll get a Fallback object with fluent syntax able to indicate and alternate source or fallback itself to fixed default.

I'm thinking to something similar from year (you can search old issues for confirmation) and I think it accommodates also @apla needs.

If you want writing something custom, you'll supply a Func delegate, I want to keep the library free from public interfaces or base classes.

The bad news is that you need to wait until 2.0 reaches stable version. Before doing it I want to add a new test project that uses FsCheck (yes the new t.p. will be in F).

Thanks everyone for joining wonderful discussion. 👍

@diev
Copy link

diev commented Aug 26, 2015

Well, this is a very elegant notation:

.Or(8080)

But it is good to know that autogenerated Settings.Designer.cs can contain a default value itself (if Property GenerateDefaultValueInCode for this Setting is set to True):

        [global::System.Configuration.ApplicationScopedSettingAttribute()]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [global::System.Configuration.DefaultSettingValueAttribute("8080")]
        public int Port {
            get {
                return ((int)(this["Port"]));
            }
        }

Unfortunately it is not allowed to simplify the notation just to use:

  [Option(Default = Properties.Settings.Default.Port)]
  public int Port { get; set; }

@gsscoder
Copy link
Owner

Yes, but this is not what I'm proposing. When Default = "Fallback", the parser will look for a property {PropertyName}Fallback that returns a Fallback type used to fluently build such default data customizations.

@diev, maybe you've looked during my process of editing...?

R. Giacomo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants