Open master0luc opened 2 years ago
Looks like problem in Orleans or Hosting/Configuration extensions packages. Maybe @Pilchie, can you please take over?
@bradygaster @ReubenBond can you help with this?
Would you mind elaborating, @master0luc? I don't quite understand.
I don't see any code which is attempting to access an "Orleans" section in configuration, or subsequently use that section.
Hello @ReubenBond, Below there is a working example, where I put commentary
namespace Platform.Rest.Api {
public class Program {
public static void Main(string[] args) {
var builder = CreateHostBuilder(args).Build();
using (var scope = builder.Services.CreateScope()) {
var service1 = scope.ServiceProvider.GetRequiredService<OrleansClusteringMigrationTask>();
service1.ExecuteAsync().Wait();
}
builder.Run();
LogManager.Flush();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseContentRoot(Directory.GetCurrentDirectory())
.UseNLog()
.ConfigureWebHostDefaults(webBuilder => {
webBuilder
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureKestrel(options => {
options.Limits.MaxRequestBodySize = 524288000;
})
//HERE WE Get our appsettings.json
.ConfigureAppConfiguration((hostContext, config) => {
var file = GetAppsettingsFile();
config.AddJsonFile(file, optional: true, reloadOnChange: true);
config.AddEnvironmentVariables();
})
.UseStartup<Startup>()
.UseSentry();
})
.UseOrleans((context, siloBuilder) => {
BuildSiloHost(context, siloBuilder);
});
private static ISiloBuilder BuildSiloHost(
Microsoft.Extensions.Hosting.HostBuilderContext context,
ISiloBuilder siloHostBuilder) {
//HERE, On Wondows I have got an object from appsettings.json
//BUT, On Linux I have got noting
var orleansSection = context.Configuration.GetSection("Orleans");
var orleansConfiguration = orleansSection.Get<OrleansConfiguration>();
Console.WriteLine($"ClusterId: {orleansConfiguration.ClusterId}");
Console.WriteLine($"ServiceId: {orleansConfiguration.ServiceId}");
siloHostBuilder
.ConfigureServices(services => {
services.Configure<OrleansConfiguration>(orleansSection);
})
.Configure<SchedulingOptions>(options => options.TurnWarningLengthThreshold = TimeSpan.FromSeconds(10))
.Configure<SiloMessagingOptions>(options => {
options.MaxMessageBodySize = int.MaxValue;
options.ClientDropTimeout = orleansConfiguration.ResponseTimeout;
options.ResponseTimeout = orleansConfiguration.ResponseTimeout;
options.ResponseTimeoutWithDebugger = orleansConfiguration.ResponseTimeoutWithDebugger;
})
.Configure<ClusterOptions>(options => {
options.ClusterId = orleansConfiguration.ClusterId;
options.ServiceId = orleansConfiguration.ServiceId;
})
.ConfigureApplicationParts(parts => parts.AddFromApplicationBaseDirectory());
if (orleansConfiguration.UseLocalhostClustering) {
siloHostBuilder.UseLocalhostClustering(
orleansConfiguration.EndpointSiloPort,
orleansConfiguration.EndpointGatewayPort
);
} else {
siloHostBuilder
.ConfigureServices(services=> {
services.AddScoped<OrleansClusteringMigrationTask>();
})
.ConfigureEndpoints(orleansConfiguration.EndpointSiloPort, orleansConfiguration.EndpointGatewayPort)
.UseAdoNetClustering(options => {
options.Invariant = orleansConfiguration.ClusterAdoInvariant;
options.ConnectionString = orleansConfiguration.ClusterConnection.ToString();
});
}
return siloHostBuilder;
}
static string GetAppsettingsFile() {
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
Console.WriteLine(environment);
if (environment.Equals("Development")) {
return $"appsettings.{environment}.json";
} else {
return $"appsettings.json";
}
}
public class OrleansConfiguration {
public string StorageName => "MDBRuntime";
public string StorageAdoInvariant => "Npgsql";
public string ClusterAdoInvariant => "Npgsql";
public PostgresConnection StorageConnection { get; set; }
public PostgresConnection ClusterConnection { get; set; }
public string ClusterId { get; set; }
public string ServiceId { get; set; }
public int EndpointSiloPort { get; set; } = 11111;
public int EndpointGatewayPort { get; set; } = 30000;
public TimeSpan ResponseTimeout { get; set; } = TimeSpan.FromSeconds(30);
public TimeSpan ResponseTimeoutWithDebugger { get; set; } = TimeSpan.FromMinutes(30);
public bool UseLocalhostClustering { get; set; }
}
public class PostgresConnection {
public string Host { get; set; }
public int Port { get; set; } = 5432;
public string Database { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string CustomSettings { get; set; } = "";
public override string ToString() {
return $"Host={Host};Port={Port};Database={Database};Username={Username};Password={Password};Persist Security Info=True;{CustomSettings}";
}
}
}
}
I wonder if this is because Linux is case-sensitive - is the file named appsettings.development.json
or appsettings.Development.json
or something else?
I doubt this is an issue with Orleans, since it doesn't touch config in any way - the host builder context is plumbed straight through.
I dubt it, because on Linux only appsettings.json is been using. And I solved this problem like this:
public class Program {
//HERE ADD A FIELD
private static IConfigurationSection _orleansSection;
public static void Main(string[] args) {
//HERE EXPLICITLY INITIALIZE
var initConfig = LoadConfiguration();
_orleansSection = initConfig.GetSection("Orleans");
var tmpOrleansConfiguration = _orleansSection.Get<OrleansConfiguration>();
Console.WriteLine($"Program: ClusterId: {tmpOrleansConfiguration.ClusterId}");
Console.WriteLine($"Program: ServiceId: {tmpOrleansConfiguration.ServiceId}");
var builder = CreateHostBuilder(args).Build();
using (var scope = builder.Services.CreateScope()) {
var service1 = scope.ServiceProvider.GetRequiredService<OrleansClusteringMigrationTask>();
service1.ExecuteAsync().Wait();
}
builder.Run();
LogManager.Flush();
}
public static IConfigurationRoot LoadConfiguration() {
var appFolder = AppDomain.CurrentDomain.BaseDirectory;
var builder = new ConfigurationBuilder()
.SetBasePath(appFolder)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
return builder.Build();
}
private static ISiloBuilder BuildSiloHost( Microsoft.Extensions.Hosting.HostBuilderContext context,ISiloBuilder siloHostBuilder) {
/HERE THE GLOBAL FIELD IS USED
var orleansConfiguration = _orleansSection.Get<OrleansConfiguration>();
...
But I have got an idea, may be this is because of the difference in the base folder of looking, perhaps: In second variant I added explicit BasePath .SetBasePath(appFolder) but in the previous case I Lost it... But again, this is an inconsistent behavior on Win and Linux... I suppose that on Windows I should have got the same problem...
Why is the code to add appsettings.json
inside the ConfigureWebHostDefaults
and not directly on the HostBuilder
?
Additionally, you are calling Host.CreateDefaultBuilder()
, which automatically adds appsettings.json
: https://github.com/dotnet/runtime/blob/c9252d5ade0cd48fbd3a92565e43b412400da5e1/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs#L188
so, You are perfectly right, according docs appsettings should be loaded, but actually they are not: after removing this block and my hack
.ConfigureAppConfiguration((hostContext, config) => {
var file = GetAppsettingsFile();
config.AddJsonFile(file, optional: true, reloadOnChange: true);
config.AddEnvironmentVariables();
})
we have this
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseNLog()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseOrleans((context, siloBuilder) => {
BuildSiloHost(context, siloBuilder);
})
.ConfigureWebHostDefaults(webBuilder => {
webBuilder
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureKestrel(options => {
options.Limits.MaxRequestBodySize = 524288000;
})
.UseStartup<Startup>()
.UseSentry();
});
private static ISiloBuilder BuildSiloHost( Microsoft.Extensions.Hosting.HostBuilderContext context, ISiloBuilder siloHostBuilder) {
**var _orleansSection = context.Configuration.GetSection("Orleans");**
var orleansConfiguration = _orleansSection.Get<OrleansConfiguration>();
and after that _orleansSection is null on Linux but not null on Windows
But, If config has got an explicit path from AppDomain.CurrentDomain.BaseDirectory:
...
var appFolder = AppDomain.CurrentDomain.BaseDirectory
...
...
.ConfigureWebHostDefaults(webBuilder => {
webBuilder
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureKestrel(options => {
options.Limits.MaxRequestBodySize = 524288000;
})
//ADD EXPLICIT PATH IN CONFIG =>
.ConfigureAppConfiguration((hostContext, config) => {
config.SetBasePath(_appFolder);
})
.UseStartup<Startup>()
.UseSentry();
...
this is strange
Problem ,perhaps, is laying there: Directory.GetCurrentDirectory() != AppDomain.CurrentDomain.BaseDirectory
Problem is there: .UseContentRoot(Directory.GetCurrentDirectory()) but Directory.GetCurrentDirectory() != AppDomain.CurrentDomain.BaseDirectory so .UseContentRoot( AppDomain.CurrentDomain.BaseDirectory) works perfectly and the root of this proble is: dotnet {parentFolder}/{folderProject}/project.dll so Directory.GetCurrentDirectory() => shows {parentFolder}/ but AppDomain.CurrentDomain.BaseDirectory => {parentFolder}/{folderProject}/
I'll leave you to it, since the cause seems to be clear now and it's unrelated to Orleans.
Thanks
Description
During Orleans Silo configuration in UseOrleans, section of "Orleans" settings is not visible on Linux, but visible on Windows:
Order of UseOrleans and ConfigureWebHostDefaults changes nothing Solution which I used is read additionaly appsettings and explicitly provide inside to BuildSiloHost config method, like this:
Configuration
Net 5.0.15 on Windows mcr.microsoft.com/dotnet/aspnet:5.0 on Linux