So extrahieren Sie eine Liste aus appsettings.json im .net-Kern

74

Ich habe eine appsettings.json-Datei, die folgendermaßen aussieht:

{
    "someSetting": {
        "subSettings": [
            "one",
            "two",
            "three"
         ]
    }
}

Wenn ich mein Konfigurationsstammverzeichnis erstelle und so etwas mache, config["someSetting:subSettings"]wird null zurückgegeben, und die tatsächlich verfügbaren Einstellungen lauten wie folgt:

config["someSettings:subSettings:0"]

Gibt es eine bessere Möglichkeit, den Inhalt someSettings:subSettingseiner Liste abzurufen ?

Devlife
quelle
Vielleicht
Venky
Vielleicht. Ich verwende eine Konsolen-App, die nicht asp.net ist, aber ich werde sehen, ob ich die Servicesammlung erhalten kann.
Devlife
Ja, das funktioniert auch in Konsolen-Apps. Es ist nichts asp.net spezifisch
Victor Hurdugaci
Ich frage nur, weil ich folgendes Could not load file or assembly 'Microsoft.Extensions.Configuration.Binder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. The system cannot find the file specified
bekomme
Sie können auch eine DTO-Klasse zum Parsen der Konfiguration verwenden
VMAtm

Antworten:

41

Sie können den Konfigurationsordner verwenden, um eine starke Typdarstellung der Konfigurationsquellen zu erhalten.

Dies ist ein Beispiel aus einem Test, den ich zuvor geschrieben habe. Ich hoffe, es hilft:

    [Fact]
    public void BindList()
    {
        var input = new Dictionary<string, string>
        {
            {"StringList:0", "val0"},
            {"StringList:1", "val1"},
            {"StringList:2", "val2"},
            {"StringList:x", "valx"}
        };

        var configurationBuilder = new ConfigurationBuilder();
        configurationBuilder.AddInMemoryCollection(input);
        var config = configurationBuilder.Build();

        var list = new List<string>();
        config.GetSection("StringList").Bind(list);

        Assert.Equal(4, list.Count);

        Assert.Equal("val0", list[0]);
        Assert.Equal("val1", list[1]);
        Assert.Equal("val2", list[2]);
        Assert.Equal("valx", list[3]);
    }

Der wichtige Teil ist der Aufruf an Bind.

Der Test und weitere Beispiele finden Sie auf GitHub

Victor Hurdugaci
quelle
Hinweis für sich selbst: Abhängig von Microsoft.Extensions.Configuration.Binder
Stefan Steiger
2
Irgendwelche Tipps, wie man das macht services.Configure<TOptions>? Ich möchte Optionen aus den Konfigurationsfeldern einfügen, die ein Array enthalten
Adam
147

Angenommen, Sie sehen so appsettings.jsonaus:

{
  "foo": {
    "bar": [
      "1",
      "2",
      "3"
    ]
  }
}

Sie können die Listenelemente wie folgt extrahieren:

Configuration.GetSection("foo:bar").Get<List<string>>()
Kirill Rakhman
quelle
6
Dies funktionierte für mich, aber ich musste zuerst das NuGet-Paket "Microsoft.Extensions.Configuration.Binder" installieren, wie hier beschrieben .
Glorfindel
und um ein Schlüsselwertobjektpaar zu erhalten, würden Sie die C # -Klassen mit json2csharp erstellen und dann Configuration.GetSection ("foo") verwenden. Holen Sie sich <List <Bar>> ()
Markus
25

In .NetCore habe ich Folgendes getan:

Normales Setup:

Erstellen Sie in Ihrer appsettings.json einen Konfigurationsabschnitt für Ihre benutzerdefinierten Definitionen:

    "IDP": [
    {
      "Server": "asdfsd",
      "Authority": "asdfasd",
      "Audience": "asdfadf"
    },
    {
      "Server": "aaaaaa",
      "Authority": "aaaaaa",
      "Audience": "aaaa"
    }
  ]

Erstellen Sie eine Klasse, um die Objekte zu modellieren:

public class IDP
{
    public String Server { get; set; }
    public String Authority { get; set; }
    public String Audience { get; set; }

}

in Ihrem Startup -> ConfigureServices

services.Configure<List<IDP>>(Configuration.GetSection("IDP"));

Hinweis: Wenn Sie innerhalb Ihrer ConfigureServices-Methode sofort auf Ihre Liste zugreifen müssen, können Sie ...

var subSettings = Configuration.GetSection("IDP").Get<List<IDP>>();

Dann in Ihrem Controller so etwas:

Public class AccountController: Controller
{
    private readonly IOptions<List<IDP>> _IDPs;
    public AccountController(IOptions<List<Defined>> IDPs)
    {
        _IDPs = IDPs;
    }
  ...
}

Nur als Beispiel habe ich es an anderer Stelle im obigen Controller wie folgt verwendet:

       _IDPs.Value.ForEach(x => {
            // do something with x
        });

Edge Case

Für den Fall, dass Sie mehrere Konfigurationen benötigen, diese sich jedoch nicht in einem Array befinden können und Sie keine Ahnung haben, wie viele Untereinstellungen Sie gleichzeitig haben werden. Verwenden Sie die folgende Methode.

appsettings.json

"IDP": {
    "0": {
      "Description": "idp01_test",
      "IDPServer": "https://intapi.somedomain.com/testing/idp01/v1.0",
      "IDPClient": "someapi",
      "Format": "IDP"
    },
    "1": {
      "Description": "idpb2c_test",
      "IDPServer": "https://intapi.somedomain.com/testing/idpb2c",
      "IDPClient": "api1",
      "Format": "IDP"
    },
    "2": {
      "Description": "MyApp",
      "Instance": "https://sts.windows.net/",
      "ClientId": "https://somedomain.com/12345678-5191-1111-bcdf-782d958de2b3",
      "Domain": "somedomain.com",
      "TenantId": "87654321-a10f-499f-9b5f-6de6ef439787",
      "Format": "AzureAD"
    }
  }

Modell

public class IDP
{
    public String Description { get; set; }
    public String IDPServer { get; set; }
    public String IDPClient { get; set; }
    public String Format { get; set; }
    public String Instance { get; set; }
    public String ClientId { get; set; }
    public String Domain { get; set; }
    public String TenantId { get; set; }
}

Erstellen Sie eine Erweiterung für das Expando-Objekt

public static class ExpandObjectExtension
    {
        public static TObject ToObject<TObject>(this IDictionary<string, object> someSource, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public)
               where TObject : class, new()
        {
            Contract.Requires(someSource != null);
            TObject targetObject = new TObject();
            Type targetObjectType = typeof(TObject);

            // Go through all bound target object type properties...
            foreach (PropertyInfo property in
                        targetObjectType.GetProperties(bindingFlags))
            {
                // ...and check that both the target type property name and its type matches
                // its counterpart in the ExpandoObject
                if (someSource.ContainsKey(property.Name)
                    && property.PropertyType == someSource[property.Name].GetType())
                {
                    property.SetValue(targetObject, someSource[property.Name]);
                }
            }

            return targetObject;
        }
    }

ConfigureServices

var subSettings = Configuration.GetSection("IDP").Get<List<ExpandoObject>>();

var idx = 0;
foreach (var pair in subSettings)
{

    IDP scheme = ((ExpandoObject)pair).ToObject<IDP>();
    if (scheme.Format == "AzureAD")
    {
        // this is why I couldn't use an array, AddProtecedWebApi requires a path to a config section
        var section = $"IDP:{idx.ToString()}";
        services.AddProtectedWebApi(Configuration, section, scheme.Description);
        // ... do more stuff
        
    }
    idx++;
}
Post Impatica
quelle
Ich habe eine Klasse zum Binden erstellt public class Definitions : List<Defined> {}. `{" Definitionen ": [{" Name ":" Somename "," Title ":" Sometitle "," Image ":" Some Image URL "}, {" Name ":" Somename "," Title ":" Sometitle " "," Image ":" some image url "}]}`
komaflash
5
var settingsSection = config.GetSection["someSettings:subSettings"];
var subSettings = new List<string>;

foreach (var section in settingsSection.GetChildren())
{
    subSettings.Add(section.Value);
}

Dies sollte Ihnen die Werte geben, die Sie benötigen, gespeichert in subSettings

Entschuldigung für das Aufrufen eines halb alten Threads. Ich hatte Schwierigkeiten, eine Antwort zu finden, da viele Methoden wie Getund veraltet sind GetValue. Dies sollte in Ordnung sein, wenn Sie nur eine einfache Lösung ohne den Konfigurationsordner benötigen. :) :)

Marc S.
quelle
0

In meinem Fall Konfiguration

 services.Configure<List<ApiKey>>(Configuration.GetSection("ApiKeysList"));

wurde nicht geladen, da die Eigenschaften schreibgeschützt waren und es keinen Standardkonstruktor gab

 **//not working** 
  public class ApiKey : IApiKey
    {
        public ApiKey(string key, string owner)
        {
            Key = key;
            OwnerName = owner;
        }
        public string Key { get;  }
        public string OwnerName { get;}
    } 

//Arbeiten

    public class ApiKey : IApiKey
    {
        public ApiKey(){}//Added default constructor
        public ApiKey(string key, string owner)
        {
            Key = key;
            OwnerName = owner;
        }
        public string Key { get; set; }        //Added set property
        public string OwnerName { get; set; }  //Added set property
    } 
Michael Freidgeim
quelle