BryanWilhite / SonghayCore

core reusable, opinionated concerns for *all* 🧐 of my C# projects
http://songhayblog.azurewebsites.net/
MIT License
1 stars 0 forks source link

the .NET Hosted Service (`IHostedService`) and the Songhay Activity (`IActivity`) #169

Open BryanWilhite opened 3 months ago

BryanWilhite commented 3 months ago

The Host Service and the Activity should work together:

[!important] The Songhay Activity depends on the configuration/input and invocation of the IConfiguration interface which is provided by the [[dotnet|.NET]] Hosted Service (and [[ASP.NET]] of course).

It follows that any feature previously developed for the Songhay Activity around configuration/input and invocation should be retired. This means:

the Songhay ActivitiesGetter class is gone 🚜πŸ”₯ #to-do

The built-in DI features of the [[dotnet|.NET]] Generic Host totally eliminates the . The very old design goal to load Activity assemblies from disk via reflection has fallen away. This means that command-line args can specify the name of an Activity type compiled in the assembly and the wonderfully elaborate system [[Microsoft]] built around .AddCommandLine(args) [πŸ“– docs ] can load args-based key-value pairs into the conventional IConfiguration [πŸ“– docs ] assigned to the command line.

This IHostedService [πŸ”— GitHub ] is loaded (perhaps) like this:

IHostBuilder builder = Host.CreateDefaultBuilder(args);

builder.ConfigureServices((hostContext, services) =>
{
    string serviceName = hostContext.Configuration["service"];

    services.AddLogging();

    switch (serviceName)  
    {  
        case "MyHostedService":  
            services.AddScoped<IRepositoryA, RepositoryA>();
            services.AddHostedService<MyHostedService>();
            break;

        case "MyOtherHostedService":
            services.AddScoped<IRepositoryB, RepositoryB>();
            services.AddHostedService<MyOtherHostedService>();
            break;
    }
});

IHost host = builder.Build();
host.Run();

This switch statement effectively replaces the ActivitiesGetter concept. The Host.CreateDefaultBuilder method [πŸ“– docs ] can load up to four different IConfiguration instances. One of these is for command-line args. I am very motivated to assert that these patterns from [[Microsoft]] eliminate the need for my ProgramArgs concept which has been propagating deeply into business logic πŸ­πŸ•³.

The code sample above is based on this little POC sketch:

using Microsoft.Extensions.Hosting;

namespace Songhay.Tests;

public class GenericHostTests
{
    [Theory]
    [InlineData("--service", "MyHostedService")]
    public void ShouldReadArgs(params string[] args)
    {
        IHostBuilder builder = Host.CreateDefaultBuilder(args);

        builder.ConfigureServices((hostContext, services) =>
        {
            string serviceName = hostContext.Configuration["service"];

            switch (serviceName)
            {
                case "MyHostedService":
                    break;
                default:
                    Assert.Fail();
                    break;
            }
        });

        IHost host = builder.Build();
        host.Run();
    }
}

the ProgramArgs help-text and conventional-parameter extensions need to be translated over into IConfiguration extensions πŸ’‘ #to-do

[[Microsoft]] has been trying to tell me this for years:

[!important] IConfiguration [πŸ“– docs ] is the cross-platform central β€˜hub’ for initial app input.

This means:

the behavior of IConfigurationExtensions.ToKeys

Since [[dotnet|.NET 6.0]], IConfiguration exposes its underlying collection via the ConfigurationExtensions.AsEnumerable extension method [πŸ“– docs ]. This late edition is probably why I have not revisited IConfiguration for a large time. But now, decades later, we have this:

string[] keys = configuration.AsEnumerable().Select(kv => kv.Key).ToArray();

…where IConfigurationExtensions.ToKeys would return keys βœ…

the behavior of IConfigurationExtensions.HasArg

The main concern of IConfigurationExtensions.HasArg is to remove any -- prefix from its string input because, even though Host.CreateDefaultBuilder does this automatically (by calling the .AddCommandLine(args) [πŸ“– docs ] extension method under the hood) for command line args pass to IConfiguration, the Songhay ProgramArgsScalars will need -- prefix removal.

After all the prefix drama is addressed, this method can search the output of IConfigurationExtensions.ToKeys with its modified its string input.

the behavior of IConfigurationExtensions.WithDefaultHelpText

This method would search the output of IConfigurationExtensions.ToKeys for all of the constants in ProgramArgsScalars and, when a key of name foo is found, it would add a new key, foo-help, to IConfiguration.