2

We are building a web app(asp.net core + angular) and we have some settings(like email server+username, mongoDb connection string, ...) that are required for the application to work properly.

The web app will be only an intranet app, and will be deployed by the customers on multiple location.

We wish that we could have a wizard when starting the web app(a bit like all those good old PHP website(wordpress like)), that would initialize the server configuration.

So my question: Is it possible, from a API call, to set some configuration(that will be persistent)? A bit like settings a config file to be reused?

EDIT With the help of @ParsaS, I managed to edit the appsettings.json, but in my program.cs, I get most of what is using my configuration, that doesn't reload those settings:

MongoDbSettings dbSettings = configuration.GetSection(MongoDbSettings.SECTION_NAME).Get<MongoDbSettings>();
        services.AddIdentityMongoDbProvider<User, Role, Guid>(
            identityOption => { identityOption.Password.RequiredLength = 8; },
            mongoOptions => { mongoOptions.ConnectionString = dbSettings.ConnectionString;});

(here typically the dbSettings.ConnectionString that I would like to edit).

Is there a way to re-run/re-start this?

J4N
  • 19,480
  • 39
  • 187
  • 340
  • where are these setting currently stored in your application ? – CodingMytra Jun 24 '22 at 06:39
  • @Nitz The app dev has just started, so for now those are in the appsettings.json but we are more than happy to change the place if it means we can change them through the service – J4N Jun 24 '22 at 06:52
  • 2
    you can make use of [Configuration Providers](https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration-providers). you can pick your initial data from appsettings.json and later it can be updated via API also with help of configuration provider – CodingMytra Jun 24 '22 at 06:56
  • you can also create custom configuration provider to read settings from some database or some locally stored file which you can update via API. – CodingMytra Jun 24 '22 at 06:58
  • @Nitz that would be great, do you have an example were the ConfigurationProvider is used to set the configuration? Is there a way to restart/reload the asp.net core server to have it taken into account? – J4N Jun 24 '22 at 07:10
  • That's beauty of configuration providers, you don't need the restart of server. it automatically reloads all the configurations when it changes. – CodingMytra Jun 24 '22 at 07:12
  • @Nitz So even the existing established mongoDb connection will be closed(and restarted) after? I'm struggling to find example on how to change the configuration through the configuration provider. Is it mandatory to have a custom configuration provider? – J4N Jun 24 '22 at 07:54
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/245874/discussion-between-nitz-and-j4n). – CodingMytra Jun 24 '22 at 07:57

3 Answers3

2

Yes the ASP is using a JSON file to store the settings (appSettings.json) so you can modify the file run-time eg.

var configFile = _environment.ContentRootFileProvider.GetFileInfo("app.settings.json").PhysicalPath;
var configJson = JsonDocument.Parse(System.IO.File.ReadAllText(configFile));
var doc = configJson.RootElement.Add("key", myData);
System.IO.File.WriteAllText(configFile, doc.RootElement.GetRawText());

public static JsonDocument Add(this JsonElement element, string property, object value)
    {
      if (element.ValueKind != JsonValueKind.Object)
        return null;

      var dic = element.GetRawText().Deserialize<Dictionary<string, object>>();
      dic[property] = value;
      return JsonDocument.Parse(dic.Serialize());
    }

have look at these documents to learn how should modify JSON objects Json Document and Json.net

Parsa S
  • 145
  • 5
  • Can you show some code? Also, I assume the startup file will not be executed so any path used at this point would not be reused? – J4N Jun 27 '22 at 18:32
  • Thanks, I will check this! I'm just not sure of something: In my Program.cs, I do setup a lot of things(logging, cors, MongoDb connection). Will this code be re-executed on every call? I tought it as done only once on startup. If it's only done once, how to refresh it to have this reuse the new config file? – J4N Jun 28 '22 at 08:38
  • sorry, I updated it again, I forgot the `Add` method is an extension I wrote it before. it is a sample code I used for my purpose and you can extend it more, I used it in the body of action so if I needed to modify configs I send a request for new data. i\m not sure how it can help you in `program.cs` so, let me know more. for refresh config, it is automatically but you can do it manually by `IConfigurationRoot` eg. `(_configuration as IConfigurationRoot).Reload();` – Parsa S Jun 28 '22 at 09:13
  • So, I ended editing the JSON with `Newtonsoft.Json`. The configuration gets edited and if I try to request something on the `IConfiguration`, I get the updated value. But in my program.cs, I setup some things, like the mongoDb connection string, and this get never called. I added this to the original question. Any idea how to reload this? – J4N Jun 28 '22 at 15:28
  • which library you are using for "AddIdentityMongoDbProvider" ? – CodingMytra Jun 30 '22 at 04:56
  • To get effect on your app, you need to shut down your app then by the first request after shutdown it will start again and it will config with new changes – Parsa S Jul 01 '22 at 09:31
  • check this [shut down ASP core](https://stackoverflow.com/questions/54912012/how-to-stop-exit-terminate-dotnet-core-hostbuilder-console-application-programma) – Parsa S Jul 01 '22 at 09:45
2

Here is what I've done:

In my Program.cs, instead of having everything, without a class or method, I created two methods and I rebuild and rerun the app when it stops:

public static void Main(string[] args)
{
    do
    {
        try
        {
            WebApplication app = Build(args);
            Run(app);
        }
        catch (Exception ex)
        { 
            Console.WriteLine(ex.ToString());
        }
    } while (true);
}

I inject in my controllers: IHostApplicationLifetime applicationLifetime

then when I need to reboot:

_applicationLifetime.StopApplication();
J4N
  • 19,480
  • 39
  • 187
  • 340
  • This is probably the safest/best way, not only because it simplifies the technical challenge, but also because it "flushes" your singleton DI services which may already initialized with the older configs – Michael Shterenberg Jul 04 '22 at 11:02
  • @MichaelShterenberg Yes, it seems to work fine. I'm just concerned that it may be difficult to "stop/kill" the app ^^ – J4N Jul 04 '22 at 12:25
1

as you know how to change appsetting file , the only thing you need is to reload your program.cs according to this answer this is how you can do it

public class Program
    {
        private static CancellationTokenSource cancelTokenSource = new System.Threading.CancellationTokenSource();
        public static void Main(string[] args)
        {
            
            var host = CreateHostBuilder(args).Build();
            host.RunAsync(cancelTokenSource.Token).GetAwaiter().GetResult();
        }
        public static void Shutdown()
        {
            cancelTokenSource.Cancel();
        }
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }

then call

Program.shutdown();
Mahdi
  • 150
  • 1
  • 8
  • There is a big "if" in this answer: **If it is behind IIS**. This just shutdown the application and IIS actually restarts it. We will deploy the application sometimes in docker, sometimes in IIS, and sometimes we just use it on debug, we wish that we can have the same behavior between the 3. Currently, I've put a `do { ... }while(true)` in the Main, so if the app get shut down, it recreates one, but I'm not sure of this cause other drawback? – J4N Jul 01 '22 at 05:15