0

I'm trying to register my custom AppSettings class as a service but it's not working and I'm lost. I have the AppSettings class and the corresponding appsettings.json file located at wwwroot. Then I have another class that depends on the AppSettings class, ctor looking like this:

public GeneratorService(AppSettings appSettings)
{
    _appSettings = appSettings;
}

The Main method looks like this:

public static async Task Main(string[] args)
{
    var builder = WebAssemblyHostBuilder.CreateDefault(args);
    builder.RootComponents.Add<App>("app");
    builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
    builder.Services.AddTransient(async sp =>
        {
            var httpClient = sp.GetRequiredService<HttpClient>();
            var response = await httpClient.GetAsync("appsettings.json");
            using var json = await response.Content.ReadAsStreamAsync();
            return await JsonSerializer.DeserializeAsync<AppSettings>(json);
        }
    );
    builder.Services.AddTransient<GeneratorService>();
    await builder.Build().RunAsync();
}

I know for sure that the JsonSerializer creates the correct instance of AppSettings from the file. I'm pretty new to this but from what I understand, it should work like this:

  1. I'm injecting GeneratorService to a .razor page so the page asks for GeneratorService instance
  2. GeneratorService has AppSettings in it's ctor so it asks for AppSettings instance
  3. Service provider knows that to get AppSettings instance it has to call HttpClient, read and deserialize the appsettings.json file

But when I try to load the mentioned .razor page, I'm getting this error:

Unhandled exception rendering component: Unable to resolve service for type 'BP.AppSettings' while attempting to activate 'BP.Services.GeneratorService'.

How come that the service provider can't create AppSettings and inject it into the GeneratorService? Thank you for your answers.

3 Answers 3

4

example appsettings.json

{
  "ClientConfigurations": {
    "AzureAd": {
      "Authority": "https://login.microsoftonline.com/001f3bfc-XXXX-XXXX-XXX-17c0e6de3b0f",
      "ClientId": "815442365ec-xxxx-xxxx-xxxx-1ce9c3f3429",
      "ValidateAuthority": true
    }
  }
}

Add these classes. Note matching names.

    public class LocalConfigurations
    {
        public ClientConfigurations ClientConfigurations { get; set; }
    }
    public class ClientConfigurations
    {
        public AzureAdConfigurations AzureAd { get; set; }
    }
    public class AzureAdConfigurations
    {
        public string Authority { get; set; }
        public string ClientId { get; set; }
        public bool ValidateAuthority { get; set; }
    }

In program.cs it is already loaded into builder.Configuration. These two lines should help you access it.

var LocalConfigurations = builder.Configuration.Get<LocalConfigurations>();    
builder.Services.AddSingleton(LocalConfigurations.ClientConfigurations);

then in any component

@page "/"
@inject Models.ClientConfigurations config


@config.AzureAd.ClientId

enter image description here

Sign up to request clarification or add additional context in comments.

Comments

1

I'm very pretty new in Blazor, but if you has an appsettings.json in root like,e.g.

{
  "message": "Hello word"
}

Just inject in your service IConfiguration, so

public WeatherForecastClient(HttpClient client, IConfiguration Configuration)
{
     Console.WriteLine(Configuration["message"]);
     this.client = client;
}

In Dane Vinson's this entry you has another aproach that it's embed the "appsettings.json" as an Embedded resource.

1.-Put the file in the root of your Blazor client project

2.-Edit your project.csproj and add some like

  <ItemGroup>
    <EmbeddedResource Include="your-file.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </EmbeddedResource>
  </ItemGroup>

3.-Create a .json and a class SettingsInfo

4.-In program.cs, you add the service

public static async Task Main(string[] args)
{
    //fileName is NameOf your Assembly.your-file.json, e.g.
    string fileName = "Blazor1.Client.appsettings.json";
    var stream = Assembly.GetExecutingAssembly()
                         .GetManifestResourceStream(fileName);

    var config = new ConfigurationBuilder()
            .AddJsonStream(stream)
            .Build();

    builder.Services.AddTransient(_ =>
    {
        return config.GetSection("AppSettings")
                     .Get<SettingsInfo>();
    });

    ....
 }

Now you can Inject SettingsInfo in your razor pages or in your services

1 Comment

An interesting way of hard coding settings ;)
0

Thank you both for your answers. After reading Orak's answer I decided to register my AppSettings instance as a singleton and now it works. So instead of:

builder.Services.AddTransient(async sp =>
    {
        var httpClient = sp.GetRequiredService<HttpClient>();
        var response = await httpClient.GetAsync("appsettings.json");
        using var json = await response.Content.ReadAsStreamAsync();
        return await JsonSerializer.DeserializeAsync<AppSettings>(json);
    }
);

I simply have:

var response = await client.GetAsync("appsettings.json");
using var json = await response.Content.ReadAsStreamAsync();
var appSettings = await JsonSerializer.DeserializeAsync<AppSettings>(json);
builder.Services.AddSingleton(appSettings);

I guess it even makes more sense to have AppSettings as a singleton instead of a transient. I'm still not sure why my original code didn't work though.

Edit: using Configuration also works:

var appSettings = builder.Configuration.Get<AppSettings>();
builder.Services.AddSingleton(appSettings.NamesLists);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.