I am using 5.7.1 and when I use modular startup UserSecrets are not added to the IConfiguration collection.
This works:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
This doesn't:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseModularStartup<Startup>()
.Build();
appSettings.json gets added to both but secrets only are getting added to standard startup.
I'm assuming it's because the surrogate Modular Startup class that's used is defined in a different assembly and the implicit behavior is to only try register secrets in the Startup assembly.
You can try registering UserSecrets explicitly:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((ctx,config) => config.AddUserSecrets<Startup>())
.UseModularStartup<Startup>()
.Build();
Related
We have FeatureFlag: IsServiceNeeded with no label set in Feature Manager of Azure App Configuration. We have used in built method AddAzureAppConfiguration by setting up the cache interval.
We are using .net core 3.1 web api and Feature Manager of Azure App Configuration.
We had IsServiceNeeded enabled during initialization of the app and after few hours, we disabled IsServiceNeed. We wait for entire day, but don't see the difference since the below returns true instead of false. We were expecting it to update every 3 minutes due to how we have it configured in program.cs file.
await _featureManager.IsEnabledAsync("IsServiceNeeded")
Let me know if you see anything odd with below. Thanks in advance,
Here is the code snippet we are using it.
Program.cs file
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
var configurationRoot = config.Build();
var appConfigConString = configurationRoot["AppConfigConnectionString"];
config.AddAzureAppConfiguration(options => options.Connect(appConfigConString).UseFeatureFlags(featureFlagOptions => {
**featureFlagOptions.CacheExpirationInterval = TimeSpan.FromMinutes(3);**
}));
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Startup.cs file
public class Startup
{
public IConfiguration Configuration { get; }
public string ContentRootPath { get; set; }
public Startup(IWebHostEnvironment env, IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddHsts(options =>
{
options.Preload = true;
options.IncludeSubDomains = true;
options.MaxAge = TimeSpan.FromDays(365);
});
var conf = Configuration.GetSection("AppSettings").Get<Config>();
services.Configure<Config>(Configuration.GetSection("AppSettings"));
services.AddSingleton<IAppSettings>(c => conf);**
services.AddScoped<IProcessHandler, ProcessHandler>();
**services.AddFeatureManagement();**
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHsts();
app.UseHttpsRedirection();
app.UseHttpStatusCodeExceptionMiddleware();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
private static void LoadMediatorHandlers(IServiceCollection services)
{
foreach (var assembly in Assembly
.GetEntryAssembly()
.GetReferencedAssemblies()
.Select(Assembly.Load)
.Where(name => (name.FullName.Contains("Queries") || name.FullName.Contains("Commands"))))
{
services.AddMediatR(assembly);
}
services.AddMediatR(typeof(Startup));
services.AddScoped<IMediator, Mediator>();
}
}
Application of Feature Flag:
public class ProcessHandler : IProcessHandler
{
private readonly IFeatureManager _featureManager;
public ProcessHandler(IFeatureManager featureManager)
{
_featureManager = featureManager;
}
public async Task<ClassA> ProcessXyz()
{
if (`await _featureManager.IsEnabledAsync("IsServiceNeeded")`)
{
return new ClassA();
}
return null;
}
}
Please Note: I have just added the required code and replaced actual names for security issues.
Thanks in advance
What is missing is the middleware that refreshes feature flags (and configuration) from Azure App Configuration.
Open your startup.cs, add below
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddAzureAppConfiguration();
services.AddFeatureManagement();
}
And then add below
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ...
app.UseAzureAppConfiguration();
}
My testing project has grown to include many AppHost classes and having to update them all when the project changes is duplicating work so I would prefer to use modular startup on them like I do with main project.
In main project I define modular startup like so:
WebHost.CreateDefaultBuilder(args)
.UseModularStartup<Startup>()
.Build();
But in my testing project I create the AppHost like this:
var appHost = new MyCustomAppHost()
.Init()
.Start(BaseUri);
and the apphost is defined like:
public class MyCustomAppHost : AppSelfHostBase
{
public MyCustomAppHost() : base(nameof(LocalProjectAppHost), typeof(MyServices).Assembly) { }
public override void Configure(Container container)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appSettings.json")
.AddEnvironmentVariables()
.AddUserSecrets(typeof(MyProject.Startup).Assembly);
var configuration = builder.Build();
//config here...
}
}
Is there a way to get modular startup working with AppSelfHostBase? My goal is to be able to specify the modular config types per AppHost like so:
public class Startup : ModularStartup
{
public Startup(IConfiguration configuration)
: base(configuration, typeof(ConfigureRedisTesting), typeof(ConfigureCorsProduction), typeof(... etc){}
}
This way I can mix and match the config files I want for this specific testing apphost and will save me copy pasting all the configs into each apphost and having to maintain them separately.
You can replace the AppSelfHostBase IWebHostBuilder Configuration by overriding ConfigureHost(). Your Startup class will also need to what the existing AppSelfHostBase.Startup does, so a custom AppHost like this should work with ServiceStack's Modular Startup feature:
public class AppHost : AppSelfHostBase {
public AppHost() : base(nameof(AppHost), typeof(MyServices).Assembly) { }
public class Startup : ModularStartup {
public Startup(IConfiguration configuration) : base(configuration, serviceTypes){}
public void ConfigureServices(IServiceCollection services) {
HostInstance.Configuration = Configuration;
HostInstance.Configure(services);
}
public virtual void Configure(IApplicationBuilder app, IHostingEnvironment env) {
HostInstance.Configure(app, env);
HostInstance.Bind(app);
((AppHost)HostInstance).RealInit();
}
}
public override IWebHostBuilder ConfigureHost(IWebHostBuilder host, string[] urlBases) {
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appSettings.json")
.AddEnvironmentVariables()
.AddUserSecrets(typeof(Startup).Assembly);
return host.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseWebRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseConfiguration(builder.Build())
.UseUrls(urlBases);
}
public override void Configure(Container container) {}
}
I am doing the webjobs using ASP.NET Core. I'm not able to set the dependency injection properly.
public static async Task Main(string[] args)
{
var host = BuildHost(args);
using (host)
{
await host.RunAsync();
}
}
public static IHost BuildHost(string[] args) =>
new HostBuilder()
.ConfigureHostConfiguration(builder =>
{
builder
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
})
.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddAzureStorage();
b.AddTimers();
})
.ConfigureServices((hostBuilderContext, services) =>
{
services.AddScoped<ISchedularService, SchedularService>();
//services.AddSingleton<IHostedService, PrintTimeService>()
}).Build();
And in the functions.cs file, I have accessed the scheduler service in the constructor. but it didn't fire the constructor and throws an exception like:
Exception thrown: 'System.InvalidOperationException' in Microsoft.Extensions.DependencyInjection
public class Functions
{
private readonly ISchedularService _schedularService;
public Functions(SchedularService schedularService)
{
_schedularService = schedularService;
}
}
Please make sure you're using the latest packages. I used your code with latest packages, and it works well.
I didn't use the async method.
static void Main(string[] args)
{
var host = BuildHost(args);
using (host)
{
host.Run();
}
}
public static IHost BuildHost(string[] args) =>
new HostBuilder()
.ConfigureHostConfiguration(builder =>
{
builder.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
})
.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddAzureStorage();
b.AddTimers();
})
.ConfigureServices((hostBuilderContext, services) =>
{
services.AddScoped<IWebJobConfiguration,WebJobConfiguration>();
})
.Build();
In Functions.cs:
private readonly IWebJobConfiguration _webJobConfiguration;
public Functions(IWebJobConfiguration webJobConfiguration)
{
Console.WriteLine("*** DI via constructor ***");
_webJobConfiguration = webJobConfiguration;
}
public void ProcessQueueMessage([QueueTrigger("queue111")] string message, ILogger logger)
{
Console.WriteLine(_webJobConfiguration.Message);
Console.WriteLine(message + ";consoleqqqq");
}
The result:
DependencyInjection problems generally occur when the wrong interface added to services. I encountered a similar problem and my code just works in .Net Core 2.x and does not work on .Net Core 3.x.
Probably there are more controls in interfaces in 3.x.
Those 2 same injections work together in 2.x but not in 3.x
services.AddScoped<IHttpClientFactory, HttpClientFactory>();
services.AddSingleton<IHttpClientFactory>(new HttpClientFactory(
Configuration["PortalLogin:Adress"],
LoginApiUserName,
LoginApiPassword));
I am building docker image and running a container from it. this container is running a .net core 2.0 web application. however after running it i get the error diplayed in the image below.
I have added this to my Program.cs already
.UseContentRoot(Directory.GetCurrentDirectory())
Old way
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.UseApplicationInsights()
.Build();
host.Run();
}
}
New Way
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
I'm trying to use connection string from my json file by doing next steps
Json file
{
"ConnectionStrings": {
"PlatformDatabase": "Server=xxxx\\SQLEXPRESS;Database=xxxx;Trusted_Connection=True;"
}
}
Access to json
var builder = new ConfigurationBuilder()
.SetBasePath(System.AppContext.BaseDirectory)
.AddJsonFile("appsettings.json",
optional: true,
reloadOnChange: true);
Configuration = builder.Build();
optionsBuilder.UseSqlServer(Configuration.GetConnectionString("PlatformDatabase"));
Error: ConfigurationBuilder does not contain a definition for
AddJsonFile.
Does anyone have this problem before? I tried searching it but all the solution I found doesn't work now (i suppose with version 2).
EDIT
note. I created .Net Core 2.0 console application
The problem was solved by install the nuget package
Microsoft.Extensions.Configuration.Json
You do not need to explicit add appsettings.json in ASP.NET Core 2.
You just need the followings inside Startup.cs -
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(
Configuration.GetConnectionString("PlatformDatabase")));
...
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
}
Program.cs
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
Another Approach
You register IConfiguration as Singleton in DI container, and then inject it to your DBContext constructor. Then get the connection string inside OnConfiguring method.
public class Startup
{
public Startup(IConfiguration config)
{
_config = config;
}
private IConfiguration _config;
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(_config);
services.AddDbContext<YOUR_DB_Context>(ServiceLifetime.Scoped);
...
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
}
}
YOUR_DB_Context.cs
public class YOUR_DB_Context : IdentityDbContext OR DbContext
{
private IConfiguration _config;
public YOUR_DB_Context(DbContextOptions options, IConfiguration config)
: base(options)
{
_config = config;
}
protected override void OnModelCreating(ModelBuilder builder)
{
...
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlServer(_config["Data:PlatformDatabase"]);
}
}