Watch2–part 6- options
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)); }
Leave a Reply