Turn `ReloadOnChange` off in config source for WebApplicationFactory - c#

This is both a question and an answer. I've fixed my problem, but it seems a bit wrong.
My original problem is running my asp.net core integration tests in a bitbucket pipeline causes System.IO.IOException: The configured user limit (128) on the number of inotify instances has been reached. Some solutions call for changing some setting through sysctl, but that is restricted by bitbucket, so that isn't an option for me.
The second way of fixing this, as noted in these stackoverflow answers, is to turn reloadOnChange off.
My new problem is now, how do we best do this for the test WebApplicationFactory?
One solution that has worked for me, which is the least amount of code, seems like a total hack. I iterate through all the JsonConfigurationSource and set ReloadOnChange to false.
Full solution:
public class TestApplicationFactory : WebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureAppConfiguration(config =>
{
foreach (var source in config.Sources)
{
if (source is JsonConfigurationSource)
{
var jsonConfigSource = (JsonConfigurationSource) source;
jsonConfigSource.ReloadOnChange = false;
}
}
});
}
}
Another solution, that I haven't tried, may be to override CreateWebHostBuilder(). However, it seems like more code and a lot of copy and paste from the default one.
Am I missing something? Is there a better way to do this?

Just experienced this issue myself running integration tests within a Linux container and followed the previous suggestions to switch off the ReloadOnChange within the WebApplicationFactory. Unfortunately that did not resolve the problem and integration tests were still failing with the same error:
System.IO.IOException: The configured user limit (128) on the number of inotify instances has been reached.
I also tried to configure xUnit to run the integration tests sequentially rather than in parallel, but that did not work either.
The solution that did work for me was to set the appropriate environment variable within the container that runs the integration tests:
export ASPNETCORE_hostBuilder__reloadConfigOnChange=false

builder.ConfigureAppConfiguration is there to configure your (main) application.
You can use builder.ConfigureHostConfiguration (see docs) to explicitly configure files to be read for the host.
builder.ConfigureHostConfiguration((hostingContext, config) =>
{
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false);
});
The host configuration is loaded. ASP.NET Core from 3.0 is built based on Generic host (rather than the Web Host of the former versions).

You can do this without inheriting from WebApplicationFactory by using the WithWebHostBuilder and ConfigureAppConfiguration extension methods:
var webAppFactory = new WebApplicationFactory<Startup>().WithWebHostBuilder(webHostBuilder =>
{
webHostBuilder.ConfigureAppConfiguration((hostingContext, configBuilder) =>
configBuilder.Sources.Where(s => s is FileConfigurationSource).ToList()
.ForEach(s => ((FileConfigurationSource)s).ReloadOnChange = false));
});
This accomplishes the same thing as your original idea (which helped me a lot!), but more compact and without the need for a separate class definition.

I just encountered the same issue.
Setting the env variable DOTNET_hostBuilder:reloadConfigOnChange to false fixed it.
This solution works for net6 when you use the Generic Host. For other hosts, maybe try replacing DOTNET_ prefix with ASPNETCORE_
To make it simple, I set it in my code before creating the WebApplicationFactory
Environment.SetEnvironmentVariable("DOTNET_hostBuilder:reloadConfigOnChange", "false");

Related

Inject AddSqlServer with another service

Is it possible to resolve an instance of ISettingsService from the ConfigureServices method in Startup(.cs) - webapplication?
I've implemented a SettingsService which is able to retrieve the database connectionstring from an external source (secure credentials store). Within the ConfigureServices I need an instance of the ISettingsService in order to get the connectionstring and pass it to the services.AddSqlServer<MyDbContext>(connectionstring) method.
While creating the instance (using var provider = services.BuildServiceProvider(); var settings = provider.GetService<ISettingsProvider>();) Visual Studio displays the next error:
Another developer posted a similar question on StackOverflow and the answer provides a solution in case of AddSingleton/ AddTransient. What is the correct way to apply it on the AddSqlServer call? Or could you provide another solution to avoid the warning/ error message?
The Intellisense comment for .AddSqlServer actually says to use .AddDbContext if you need more control, and that's certainly the correct option.
If you refer to the source code here, you can see that all .AddSqlServer is actually doing is calling .AddDbContext and configuring the options accordingly. We can therefore write our own solution like this:
services.AddDbContext<DbContext>((serviceProvider, options) => {
var settings = serviceProvider.GetService<ISettingsProvider>();
// I don't know what methods your ISettingsProvider makes available
// so adjust accordingly
string connectionString = settings.GetSetting("connectionString");
options.UseSqlServer(connectionString);
});
Of course you can make other changes to the options here, and .UseSqlServer can also take a Action<SqlServerDbContextOptionsBuilder> (options.UseSqlServer(connectionString, opts => opts.EnableRetryOnFailure()), etc.) to further configure it.

C# AWS Parameter Store - Configuration not loading SystemManagerConfiguration in .Net 6

Application is not able to talk to AWS Parameter Store in .NET 6.
It is always talking to appsettings.json.
I tried debugging locally, still same behavior. Not able to find the SystemManagerConfiguration under list of configuration .
var builder = WebApplication.CreateBuilder();
var connectionString = builder.Configuration.GetConnectionString("OrderTrackerDatabase");
Packages Used
Library Source Code : https://github.com/aws/aws-dotnet-extensions-configuration
image
I got the same issue and finally resolved it.
The samples code in https://github.com/aws/aws-dotnet-extensions-configuration missed one line as below after called "AddSystemsManager" method in .Net 6.
builder.Services.Configure<Settings>(builder.Configuration.GetSection($"common:settings"));
After added above line, then I'm able to get the correct values from AWS Parameter Store when using the settings.
I've also created an issue of this in GitHub as below -
https://github.com/aws/aws-dotnet-extensions-configuration/issues/114
I believe the problem might be the trailing slash after "/OrderTracking/", try "/OrderTracking" instead.
WebApplication.CreateBuilder() will create new instance and doesn't carry over the SystemManager configuration.
Instead, use IConfiguration instance through constructor DI.
var connectionString = _configuration.GetConnectionString("OrderTrackerDatabase");
In my case this extensions method was returning null at my lambda:
private static IConfiguration InitializeConfiguration() => new ConfigurationBuilder()
.AddSystemsManager($"/my-data", true, TimeSpan.FromMinutes(5))
.Build();
Because the role of lambda didn't have permission for read SSM for that resource.
User: is not authorized to perform: ssm:GetParametersByPath on resource
So, just add necessary permission (ssm:GetParametersByPath) for the role of lambda to your resource at System Manager.
In my case, I am using lambda serverless, so the IConfig is always null when it is passed to the controller.
I resolved it by changing the IOptions<Settings> settings in the Controller constructor to IConfiguration settings and then access the parameters by name like _settings.GetValue<string>("ParameterName")
A little less "Object Oriented", but it seemed much easier than this complex solution

get application path in asp.net vnext

I've been trying to open a file in asp.net 5 and have not been having a lot of success.
In the past you used Server.MapPath or HostingEnvironment.ApplicationPhysicalPath. They are both gone in the OWIN based framework.
There is a HostingEnvironment class but it's in the System.Aspnet but it needs to be initialized by the hosting environment (it no longer has a static member for ApplicationPhysicalPath but I'm guessing the WebRoot member does that now. The problem is I can't find a reference to it anywhere.
I've also looked at Context.GetFeature<> but it doesn't seem to have any feature that would show the application path, just request and response related info. The code listing the features can be found here.
<snark>Is the ability to work with files a discontinued feature in ASP.NET?</snark>
There is also the possibility that I can't brain very well right now and missed something.
You can get it from the ApplicationBasePath property of Microsoft.Framework.Runtime.IApplicationEnvironment serivce.
Example: https://github.com/aspnet/Mvc/blob/9f1cb655f6bb1fa0ce1c1e3782c43a2d45ca4e37/test/WebSites/FilesWebSite/Controllers/DownloadFilesController.cs#L28
There are two approaches now:
using Microsoft.Extensions.PlatformAbstractions;
public Startup(IApplicationEnvironment appEnv)
{
// approach 1
var path01 = PlatformServices.Default.Application.ApplicationBasePath;
// approach 2
var path02 = appEnv.ApplicationBasePath;
}

How to reconfigure SQLTransport based NServicebus in Asp.net Web API?

I am using NServicebus(version 4.6.3) with SQLTransport in my ASP.net web api project. I have different connectionstrings for the queues for different environments (Dev,QA,etc). My configuration looks like below:
public class BusConfigurator
{
public static IStartableBus Bus { get; private set; }
public static void DisposeBus()
{
if (Bus == null)
return;
Bus.Shutdown();
Bus.Dispose();
Bus = null;
}
public static void InitializeServiceBus(string connectionString)
{
var configure = Configure.With()
.DefineEndpointName("MyEndPoint")
.Log4Net(new DebugAppender { Threshold = Level.Warn })
.UseTransport<SqlServer>(connectionString)
.PurgeOnStartup(false)
.SetDefaultTransactionLevel()
.UnicastBus(); // Error is thrown here on second call
configure.MyCustomSQLServerPersistence();
Bus = configure.CreateBus();
}
public static void StartBus()
{
Bus.Start(() => Configure.Instance.ForInstallationOn<NServiceBus.Installation.Environments.Windows>().Install());
}
}
I have a dropdown in the app so that the user can select the environment. Based on the selection, I want to reconfigure the bus. So, I call DisposeBus then pass the connection string to the IntializeServiceBus method followed by the startBus. It works first time but throws error below when it gets called again with different connectionstring:
Unable to set the value for key: NServiceBus.Transport.ConnectionString. The settings has been locked for modifications. Please move any configuration code earlier in the configuration pipeline
Source=NServiceBus.Core
Line=0
BareMessage=Unable to set the value for key: NServiceBus.Transport.ConnectionString. The settings has been locked for modifications. Please move any configuration code earlier in the configuration pipeline
Is NServicebus intended to be used/configured this way? (I am guessing probably not) If not then is there a workaround/different approach for this?
In V4 or below, there is no way to do it by normal human means. There is only one Bus per AppDomain. All of the configuration API is static, so if you try, you get exactly the problems you ran into.
By "human means", I mean that it might be possible to do something crazy with spinning up a new AppDomain within your process, setting up a Bus within that, and then tearing it down when you're finished. It might be possible. I haven't tried it. I wouldn't recommend it.
In V5, the configuration API is completely redesigned, is not static, and so this is possible:
var cfg = new BusConfiguration();
// Set up all the settings with the new V5 Configuration API
using (var justOneBus = NServiceBus.Bus.Create(cfg).Start())
{
// Use justOneBus, then it gets disposed when done.
}
That's right. It's disposable. Then you can do it again. In your case you wouldn't want to put it in a using block - you would want to set it up somewhere, and when the dropdown gets switched, call Dispose on the current instance and rebuild it with the new parameters.
Keep in mind, however, that the Bus is still pretty expensive to create. It's definitely still something you want to treat as an application-wide singleton (or singleton-like) instance. You definitely wouldn't want to spin up a separate one per web request.

WCF ServiceHost already has 5 behaviors

I´m creating a ServiceFactory to gain control over inicialization of my services exposed through IIS 7.
However I´m surprised by the behavior of ServiceHost. Although I have 0 configuration files for the service, wherever I Initialize a new ServiceHost, like this:
var host = new ServiceHost(typeof(MyService), baseAddresses);
Next I want to add some behaviors only if the build is in Debug mode:
#if DEBUG
host.Description.Behaviors.Add(new ServiceDebugBehavior());
#endif
However this code fails cause the ServiceDebugBehavior is already applied! Despite I have no configuration files, and no attributes applied to the service class, the host already has this behavior and 5 more applied!
Is this the expected behavior? What if I want to disable the ServiceDebugBehavior on release builds?
Thanks in advance,
Not easily - no setting I'm aware of to just turn this off. Question really is: what benefit do you get from that??
From what I see, most of those behaviors are quite essential - authentication and service credentials and so forth. And if they're there by default, even without config, I would believe they're there for a reason.
But if you really really want to, you can always create your own CustomServiceHost and do whatever you like inside that class - including tossing out all pre-defined behaviors, if you want to.
If you want to e.g. enable the IncludeExceptionDetailsInFaults setting on the service debug behavior of your service, try this type of code:
ServiceDebugBehavior behavior =
host.Description.Behaviors.Find<ServiceDebugBehavior>();
if(behavior != null)
{
behavior.IncludeExceptionDetailInFaults = true;
}
else
{
host.Description.Behaviors.Add(
new ServiceDebugBehavior() { IncludeExceptionDetailInFaults = true });
}
In this case, if the ServiceDebugBehavior is present already, you find it and just set the property to true - otherwise you create and add a new ServiceDebugBehavior. Pretty easy, I think.
You shouldn't create the service debug behaviour inside the #if DEBUG, instead just set the values for the properties you want to change from the default.

Categories