J'essayais d'appeler GetSection
à partir de la configuration injectée dans le Startup.cs
. La valeur était null
, alors que indexer
à une valeur de section concrète renvoie non-null
valeur. Il me semble un bug derrière la méthode GetSection
ou je me trompe?
appsettings.json:
{"MyConfig": { "ConfigA": "valeurA", "ConfigB": "valueB"}}
Program.cs:
public static void Main(string[] args)
{
var Host = BuildWebHost(args);
Host.Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
Startup.cs:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
var mySection = this.Configuration.GetSection("MyConfig");
var myVal = this.Configuration["MyConfig:ConfigA"];
J'ai tout d'abord vérifié s'il y avait des changements entre 1.1.1 et 2.x de JsonConfigurationProvider.cs
, le code interne qui construit en définitive les valeurs récupérables à partir de votre fichier JSON. Il n'y a eu aucun changement à ce code ni à aucun autre code qui a finalement été appelé par votre this.Configuration.GetSection("MyConfig");
.
Pour récupérer des valeurs, la méthode Configuration
cherchera votre clé MyConfig
dans chaque fournisseur de configuration dans l'ordre inverse, comme indiqué dans le code, jusqu'à ce qu'une valeur soit trouvée. Dans votre exemple, les fournisseurs (json, variables d’envois, arguments de ligne de commande) sont fournis dans Webhost.CreateDefaultBuilder()
(voir ici) .
En regardant le code pour JsonConfigurationFileParser.cs , il crée un Dictionary<string, string>
pour les clés et les valeurs, mais uniquement pour les valeurs primitives. C'est-à-dire qu'aucune clé n'est stockée pour MyConfig
(à ce niveau, il s'agit d'un objet), mais il y aura une clé pour MyConfig:ConfigA
et les valeurs d'un tableau ressembleraient à MyConfig:ConfigA:0
, MyConfig:ConfigA:1
, etc.
Enfin, vous découvrirez que Configuration.GetSection("MyConfig")
_ { vous renvoie toujours une nouvelle construction } _ ConfigurationSection
qui est never null, et au pire aura une Value
propriété de null
.
Ainsi, que se passe-t-il lorsque vous survolez une ConfigurationSection
avec Intellisense et que vous examinez la propriété Value
, c’est que chaque fournisseur de configuration a fait l’objet d’une recherche et qu’il n’a pas été détecté que la clé "MyConfig" était convertie en chaîne pour retourner .
Vous aurez au moins besoin d'appeler:
services.Configure<MyConfigOptions>(configuration.GetSection("MyConfig"));
services.AddSingleton(cfg => cfg.GetService<IOptions<MyConfigOptions>>().Value);
de l'injecter dans l'ensemble de votre application en tant qu'objet C #. Sinon, appelez des valeurs individuelles avec la syntaxe de séparateur deux-points ["MyConfig:ConfigA"]
ou avec var mySection = this.Configuration.GetSection("MyConfig")["ConfigA"];
, qui est redondant mais l'illustre. Il n'est utilisé que pour récupérer des primitives.
Pour lier des objets C # et les injecter, j'ai créé la méthode d'extension suivante:
public static class IServiceCollectionExtensions
{
public static IServiceCollection AddConfigOptions<TOptions>(this IServiceCollection services,
IConfiguration configuration, string section) where TOptions : class, new()
{
services.Configure<TOptions>(configuration.GetSection(section));
services.AddSingleton(cfg => cfg.GetService<IOptions<TOptions>>().Value);
return services;
}
}
qui peut s'appeler comme ceci:
public class Startup
{
public Startup(IConfiguration configuration) => Configuration = configuration;
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddConfigOptions<EmailOptions>(Configuration, "Email")
et injecté comme ceci:
public class EmailSender : IEmailSender
{
private EmailOptions _emailOptions;
public EmailSender(EmailOptions options) => _emailOptions = options;
J'ai trouvé AspNetCore 2.0 tellement plus simple que 1.x en termes de configuration.
Si vous mettez un point d'arrêt dans le constructeur Startup(IConfiguration configuration)
, vous remarquerez que la variable configuration
a environ 5 fournisseurs.
JsonConfigurationProvider
est le fournisseur qui vous intéresse pour le fichier appsettings.json, mais vous remarquerez probablement que la propriété Data
a un Count=0
. Cela est probablement dû au fait que votre application recherche le fichier appsettings.json dans le répertoire spécifié dans JsonConfigurationProvider.Source.FileProvider.Root
(qui est par défaut wwwroot).
J'ai simplement ajouté une tâche MSBuild dans le fichier .csproj comme suit:
<Copy SourceFiles="appsettings.json" DestinationFolder="wwwroot" />
Et cela semblait fonctionner parfaitement.
Ceci n’est évidemment utile que lors du développement local uniquement . Cependant, la pratique générale de nos jours est de ne jamais avoir de configuration dans un fichier en premier lieu, d'autant plus que cela finira par faire partie de votre historique de prise en pension. Au lieu de cela, lors du déploiement de votre application, les deux pratiques courantes de nos jours utilisent des variables d'environnement pour remplacer vos valeurs, voire mieux, en utilisant un magasin clé/valeur comme consul.
De plus, je vois toute une série d'exemples en ligne sur la façon d'utiliser la fonction services.Configure<>()
, ce qui est correct, mais le Startup.cs
utilise déjà DI pour injecter des valeurs dans IConfiguration configuration
. Cela signifie que IConfiguration
est déjà enregistré dans le conteneur IoC.. Cela signifie donc que vous pouvez déjà utiliser IConfiguration
dans toute autre classe de votre application, de la manière suivante:
public JobsRepository(IConfiguration configuration)
{
this.configA = configuration.GetSection("MyConfig:ConfigA").Value;
}
Dans mon cas, il me manquait un paquet:
Microsoft.Extensions.Configuration.Binder
C'est en fait documenté comme un commentaire de code subtil dans ici