Skip to content
This repository has been archived by the owner on Dec 18, 2018. It is now read-only.

Add back Get<T>() extension method to bind objects to configuration values or entire sections #532

Closed
tugberkugurlu opened this issue Oct 21, 2016 · 9 comments
Assignees
Milestone

Comments

@tugberkugurlu
Copy link

tugberkugurlu commented Oct 21, 2016

I am not sure where I am doing something wrong but I am seeing a weird behaviour in terms of getting parsing the JSON config file.

I would appreciate if someone can have a look and tell me where I am doing something stupid 😞

Repro Steps

git clone git@github.com:tugberkugurlu/AspNetCoreSamples.git
cd AspNetCoreSamples/repros/ConfigSectionNull
git checkout -qf 4101a06ab9ab05ac15ed2824a44ff97c323d371e
dotnet restore
dotnet run

Expected Result

Console output to be equal to below:

'MySecondNode' value is null? False
'RabbitMQ' value is null? False

Actual Result

Console output is equal to below:

'settings' is null
'MySecondNode' value is null? True
'RabbitMQ' value is null? True

dotnet CLI Info

Tugberks-MacBook-Pro:ConfigSectionNull tugberk$ dotnet --info
.NET Command Line Tools (1.0.0-preview2-003121)

Product Information:
 Version:            1.0.0-preview2-003121
 Commit SHA-1 hash:  1e9d529bc5

Runtime Environment:
 OS Name:     Mac OS X
 OS Version:  10.12
 OS Platform: Darwin
 RID:         osx.10.12-x64

Source

Program.cs:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.PlatformAbstractions;

namespace rabbitsample
{
    public static class Program 
    {   
        public static void Main(string[] args)
        {
            var config = ConfigBuilder.Build();

            var settings = config.GetValue<RabbitMQSettings>("RabbitMQ");
            if(settings == null) 
            {
                System.Console.WriteLine("'settings' is null");
            }

            var cildrenNodes = config.GetChildren();
            foreach (var item in cildrenNodes)
            {
                System.Console.WriteLine($"'{item.Key}' value is null? {item.Value == null}");
            }
        }
    }

    public class RabbitMQSettings 
    {
        public string Host { get; set; }
    }

    public static class ConfigBuilder
    {   
        public static IConfiguration Build()
        {
            return new ConfigurationBuilder()
                .SetBasePath(PlatformServices.Default.Application.ApplicationBasePath)
                .AddJsonFile("config.json")
                .AddEnvironmentVariables("rabbitsample_")
                .Build();
        }
    }
}

project.json:

{
    "version": "1.0.0-*",
    "buildOptions": {
        "debugType": "portable",
        "emitEntryPoint": true,
        "copyToOutput": ["config.json"]
    },
    "dependencies": {
        "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
        "Microsoft.Extensions.Configuration.Json": "1.0.0",
        "Microsoft.Extensions.Configuration.Binder": "1.0.0",
        "Microsoft.Extensions.PlatformAbstractions": "1.0.0",
        "Microsoft.Extensions.Options": "1.0.0",
        "Lorem.DNX.NET": "1.0.40",
        "easynetq": "2.0.2-netcore0001"
    },
    "frameworks": {
        "netcoreapp1.0": {
            "dependencies": {
                "Microsoft.NETCore.App": {
                    "type": "platform",
                    "version": "1.0.0"
                }
            },
            "imports": "dnxcore50"
        }
    }
}

config.json:

{
    "RabbitMQ": {
        "Host": "localhost"
    },

    "MySecondNode": {
        "Value1": "1",
        "Value2": "2"
    }
}
@tugberkugurlu
Copy link
Author

This made it work: tugberkugurlu/AspNetCoreSamples@87cfde9

That's weird though. Is GetValue<T> only supposed to work for simple types?

@Rick-Anderson
Copy link

Here's the sample I've added to the config doc PR

using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;

// Add NuGet  <package id="Microsoft.Extensions.Configuration.Binder"
public class Program
{   
    static public IConfigurationRoot Configuration { get; set; }
    public static void Main(string[] args = null)
    {
        var dict = new Dictionary<string, string>
            {
                {"Profile:MachineName", "Rick"},
                {"App:MainWindow:Height", "11"},
                {"App:MainWindow:Width", "11"},
                {"App:MainWindow:Top", "11"},
                {"App:MainWindow:Left", "11"}
            };

        var builder = new ConfigurationBuilder();
        builder.AddInMemoryCollection(dict);
        Configuration = builder.Build();
        Console.WriteLine($"Hello {Configuration["Profile:MachineName"]}");

        // Show GetValue overload and set the default value to 80
        // You typically would set default values in the constructor.
        // Requires NuGet package "Microsoft.Extensions.Configuration.Binder"
        var left = Configuration.GetValue<int>("App:MainWindow:Left", 80);
        Console.WriteLine($"Left {left}");

        var window = new MyWindow();
        Configuration.GetSection("App:MainWindow").Bind(window);
        Console.WriteLine($"Left {window.Left}");
    }
}

@tugberkugurlu
Copy link
Author

thanks @Rick-Anderson!

After getting help from Twitter people and digging into the source code, I discovered that GetValue<T> API is not what I wanted. I think it only works for certain types if I am not mistaken (e.g. simple types like int, string, etc.) and it will never work for something like MySettings, correct?

It could have been better if it gave feedback about those situations rather than just returning null directly (e.g. throw saying that it cannot convert that type, etc.). However, I would more prefer for it to use ConfigurationBinder.Bind under the hood and do the right thing.

@HaoK
Copy link
Member

HaoK commented Oct 25, 2016

Yep it's basically a convert. It's been a common source of confusion so far.

On Oct 21, 2016, at 1:41 PM, Tugberk Ugurlu notifications@github.com wrote:

This made it work: tugberkugurlu/AspNetCoreSamples@87cfde9

That's weird though. Is GetValue only supposed to work for simple types?


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or mute the thread.

@HaoK
Copy link
Member

HaoK commented Oct 25, 2016

After discussion with @divega @glennc we've decided to bring back Get<T>() which will 'do the right thing' for the majority of cases where T is convertible/bindable from either the Value/Children.

We'll need to hash out some of the specific details for some of the edge cases and the heuristics, but that discussion can go in the PR.

@HaoK HaoK changed the title Cannot get values correctly from JSON config file Bring back Get<T>() [RIP T Bind<T>] Oct 25, 2016
@HaoK HaoK added this to the 1.1.0 milestone Oct 25, 2016
@HaoK
Copy link
Member

HaoK commented Oct 25, 2016

Also this will eliminate the need for the T Bind<T>() that we added in 1.1-preview1, I'll remove that method as part of this change

@tugberkugurlu
Copy link
Author

@HaoK 🎉 🎉 thanks a lot!

@HaoK
Copy link
Member

HaoK commented Oct 25, 2016

Associated PR for this work: #535

@HaoK
Copy link
Member

HaoK commented Nov 1, 2016

d73b261

@HaoK HaoK closed this as completed Nov 1, 2016
@HaoK HaoK added 3 - Done and removed 2 - Working labels Nov 1, 2016
@HaoK HaoK self-assigned this Nov 1, 2016
@divega divega changed the title Bring back Get<T>() [RIP T Bind<T>] Add back Get<T>() extension methods to bind objects to configuration values or entire sections Nov 16, 2016
@divega divega changed the title Add back Get<T>() extension methods to bind objects to configuration values or entire sections Add back Get<T>() extension method to bind objects to configuration values or entire sections Nov 16, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants