The Watch2 package passes all the command line arguments to the dotnet watch. So how can it have its own settings? Simple: by reading a watch.json file that is in the same folder where it is executing.
1. Testing
I was considering how to perform the testing, and there were two options:
Microsoft File Providers: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/file-providers?view=aspnetcore-8.0
TestableIO: https://github.com/TestableIO/System.IO.Abstractions
I thought that the second option (TestableIO) would be easier to use and more flexible. However, I do not need to test the creation of the file, but rather the content of the file. Therefore, I will use the first option (Microsoft File Providers).
2. Maintain JSON in Sync with C#
I need a solution to transform JSON to C# on the fly (i.e., at compile time). For this, a Roslyn Code Generator can be used. The rscgutils NuGet package will be helpful for this purpose.
Here is the relevant configuration in the project file:
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="8.0.0" />
<PackageReference Include="rscgutils" Version="2024.2000.2000" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Watch2_Interfaces\Watch2_Interfaces.csproj" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="options.gen.json" />
</ItemGroup>
Code in Program.cs
The code in Program.cs will utilize the above configurations and packages. Here is an example of how it might look:
This example demonstrates how to read the watch.json file using the Microsoft File Providers package. The rscgutils package will handle the transformation of JSON to C# at compile time, ensuring that the JSON configuration is always in sync with the C# code.
So the code in program.cs will be:
//test
//args = ["run --no-hot-reload"];
//string folder = @"D:\gth\RSCG_Examples\v2\Generator";
//uncomment this line for production
string folder = Environment.CurrentDirectory;
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection, folder);
var serviceProvider = serviceCollection.BuildServiceProvider();
var console = serviceProvider.GetRequiredService<IConsoleWrapper>();
var processManager = serviceProvider.GetRequiredService<ProcessManager>();
var startInfo = serviceProvider.GetRequiredService<IProcessStartInfo>();
var fileOptions = serviceProvider.GetRequiredService<IOptionsReader>();
if(!fileOptions.ExistsFile())
{
var file = Path.Combine(folder, "watch2.json");
File.WriteAllText(file, MyAdditionalFiles.options_gen_json);
}
await processManager.StartProcessAsync(args, console, startInfo);
void ConfigureServices(IServiceCollection services,string folder)
{
services.AddSingleton<IFileProvider>(new PhysicalFileProvider(folder));
services.AddSingleton<IOptionsReader, OptionsReader>();
services.AddSingleton<Ioptions_gen_json>(it =>
{
var optionsReader = it.GetRequiredService<IOptionsReader>();
return optionsReader.GetOptions() ?? options_gen_json.Empty;
});
services.AddSingleton<IConsoleWrapper, ConsoleWrapper>();
services.AddSingleton<ProcessManager, ProcessManager>();
services.AddSingleton<IProcessStartInfo>(provider => new ProcessStartInfoWrapper
{
FileName = "dotnet",
Arguments = "watch " + string.Join(' ', args),
WorkingDirectory = folder,
RedirectStandardOutput = true,
RedirectStandardInput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
});
services.AddLogging(loggingBuilder =>
{
loggingBuilder.ClearProviders();
loggingBuilder.SetMinimumLevel(LogLevel.Trace);
loggingBuilder.AddNLog("nlog.config");
});
services.AddSingleton<ILogger<ProcessManager>, Logger<ProcessManager>>();
services.AddSingleton<Func<IProcessStartInfo, IProcessWrapper>>(it => new ProcessWrapper(it));
}