< Summary

Information
Class: NexusLabs.Needlr.Hosting.HostApplicationBuilderNeedlrExtensions
Assembly: NexusLabs.Needlr.Hosting
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Hosting/HostApplicationBuilderNeedlrExtensions.cs
Line coverage
94%
Covered lines: 63
Uncovered lines: 4
Coverable lines: 67
Total lines: 233
Line coverage: 94%
Branch coverage
75%
Covered branches: 6
Total branches: 8
Branch coverage: 75%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
UseNeedlrDiscovery(...)100%22100%
RunHostPlugins(...)100%22100%
RunHostApplicationBuilderPlugins(...)50%2283.33%
RegisterHostPlugins(...)50%2281.81%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Hosting/HostApplicationBuilderNeedlrExtensions.cs

#LineLine coverage
 1using Microsoft.Extensions.DependencyInjection;
 2using Microsoft.Extensions.Hosting;
 3using Microsoft.Extensions.Logging;
 4using Microsoft.Extensions.Logging.Abstractions;
 5
 6using NexusLabs.Needlr.Injection;
 7
 8using System.Reflection;
 9
 10namespace NexusLabs.Needlr.Hosting;
 11
 12/// <summary>
 13/// Extension methods for integrating Needlr discovery into user-controlled <see cref="HostApplicationBuilder"/> instanc
 14/// Use this when you want to maintain control over the host creation process while still benefiting from Needlr's
 15/// automatic service discovery and plugin system.
 16/// </summary>
 17/// <remarks>
 18/// <para>
 19/// <see cref="UseNeedlrDiscovery"/> provides a "reverse integration" approach where Needlr adapts to your builder
 20/// rather than you adapting to Needlr. This is useful when:
 21/// </para>
 22/// <list type="bullet">
 23///     <item>You have existing host configuration code you want to preserve</item>
 24///     <item>You need fine-grained control over the builder lifecycle</item>
 25///     <item>You're integrating Needlr into a larger framework</item>
 26/// </list>
 27/// <para>
 28/// Note: <see cref="IHostPlugin"/> plugins are NOT executed by <see cref="UseNeedlrDiscovery"/> because the user
 29/// controls when <see cref="HostApplicationBuilder.Build"/> is called. If you need IHostPlugin support,
 30/// call <see cref="RunHostPlugins"/> after building the host.
 31/// </para>
 32/// </remarks>
 33/// <example>
 34/// Basic usage with user-controlled builder:
 35/// <code>
 36/// var builder = Host.CreateApplicationBuilder(args);
 37///
 38/// // Your existing configuration
 39/// builder.Services.AddMyCustomServices();
 40///
 41/// // Add Needlr discovery - runs IHostApplicationBuilderPlugin and IServiceCollectionPlugin
 42/// builder.UseNeedlrDiscovery();
 43///
 44/// // More of your configuration
 45/// builder.Services.AddOtherServices();
 46///
 47/// var host = builder.Build();
 48///
 49/// // Optionally run IHostPlugin plugins
 50/// host.RunHostPlugins();
 51///
 52/// await host.RunAsync();
 53/// </code>
 54/// </example>
 55public static class HostApplicationBuilderNeedlrExtensions
 56{
 57    /// <summary>
 58    /// Integrates Needlr's automatic service discovery and plugin system into the <see cref="HostApplicationBuilder"/>.
 59    /// </summary>
 60    /// <param name="builder">The host application builder to configure.</param>
 61    /// <param name="syringe">
 62    /// Optional syringe instance to use for configuration. If not provided, a default syringe is created.
 63    /// Use this to provide a pre-configured syringe with specific type registrars or filterers.
 64    /// </param>
 65    /// <param name="logger">Optional logger for discovery and plugin execution logging.</param>
 66    /// <returns>The same <see cref="HostApplicationBuilder"/> for method chaining.</returns>
 67    /// <remarks>
 68    /// <para>This method performs the following in order:</para>
 69    /// <list type="number">
 70    ///     <item>Discovers assemblies using the syringe's assembly provider</item>
 71    ///     <item>Runs all <see cref="IHostApplicationBuilderPlugin.Configure"/> methods</item>
 72    ///     <item>Runs all <see cref="IServiceCollectionPlugin.Configure"/> methods</item>
 73    ///     <item>Registers discovered types to the service collection</item>
 74    /// </list>
 75    /// <para>
 76    /// <see cref="IHostPlugin"/> plugins are NOT executed. If needed, call <see cref="RunHostPlugins"/>
 77    /// after <see cref="HostApplicationBuilder.Build"/>.
 78    /// </para>
 79    /// </remarks>
 80    /// <example>
 81    /// With a pre-configured syringe:
 82    /// <code>
 83    /// var syringe = new Syringe()
 84    ///     .UsingReflection()
 85    ///     .UsingAdditionalAssemblies(typeof(MyPluginAssembly).Assembly);
 86    ///
 87    /// var builder = Host.CreateApplicationBuilder(args);
 88    /// builder.UseNeedlrDiscovery(syringe);
 89    /// </code>
 90    /// </example>
 91    public static HostApplicationBuilder UseNeedlrDiscovery(
 92        this HostApplicationBuilder builder,
 93        ConfiguredSyringe syringe,
 94        ILogger? logger = null)
 95    {
 796        ArgumentNullException.ThrowIfNull(builder);
 697        ArgumentNullException.ThrowIfNull(syringe);
 98
 699        logger ??= NullLogger.Instance;
 100
 6101        var typeRegistrar = syringe.GetOrCreateTypeRegistrar();
 6102        var typeFilterer = syringe.GetOrCreateTypeFilterer();
 6103        var pluginFactory = syringe.GetOrCreatePluginFactory();
 6104        var serviceCollectionPopulator = syringe.GetOrCreateServiceCollectionPopulator(typeRegistrar, typeFilterer, plug
 6105        var assemblyProvider = syringe.GetOrCreateAssemblyProvider();
 6106        var additionalAssemblies = syringe.GetAdditionalAssemblies();
 107
 6108        var serviceProviderBuilder = syringe.GetOrCreateServiceProviderBuilder(
 6109            serviceCollectionPopulator,
 6110            assemblyProvider,
 6111            additionalAssemblies);
 112
 6113        var candidateAssemblies = serviceProviderBuilder.GetCandidateAssemblies();
 114
 115        // Run IHostApplicationBuilderPlugin plugins
 6116        RunHostApplicationBuilderPlugins(
 6117            builder,
 6118            pluginFactory,
 6119            candidateAssemblies,
 6120            logger);
 121
 122        // Register discovered services
 6123        logger.LogInformation("Registering discovered services to service collection...");
 6124        serviceCollectionPopulator.RegisterToServiceCollection(
 6125            builder.Services,
 6126            builder.Configuration,
 6127            candidateAssemblies);
 6128        logger.LogInformation("Registered discovered services to service collection.");
 129
 130        // Store the service provider builder for post-build plugin execution
 6131        builder.Services.AddSingleton(serviceProviderBuilder);
 6132        builder.Services.AddSingleton(pluginFactory);
 6133        builder.Services.AddSingleton<IReadOnlyList<Assembly>>(candidateAssemblies);
 134
 6135        return builder;
 136    }
 137
 138    /// <summary>
 139    /// Runs <see cref="IHostPlugin"/> plugins on the built host.
 140    /// Call this after <see cref="HostApplicationBuilder.Build"/> if you need IHostPlugin support.
 141    /// </summary>
 142    /// <param name="host">The built host to configure.</param>
 143    /// <param name="logger">Optional logger for plugin execution logging.</param>
 144    /// <returns>The same <see cref="IHost"/> for method chaining.</returns>
 145    /// <remarks>
 146    /// This method retrieves the plugin factory and candidate assemblies that were stored
 147    /// during <see cref="UseNeedlrDiscovery"/> and uses them to run IHostPlugin plugins.
 148    /// </remarks>
 149    /// <example>
 150    /// <code>
 151    /// var builder = Host.CreateApplicationBuilder(args);
 152    /// builder.UseNeedlrDiscovery();
 153    ///
 154    /// var host = builder.Build();
 155    /// host.RunHostPlugins(); // Run IHostPlugin plugins
 156    ///
 157    /// await host.RunAsync();
 158    /// </code>
 159    /// </example>
 160    public static IHost RunHostPlugins(
 161        this IHost host,
 162        ILogger? logger = null)
 163    {
 2164        ArgumentNullException.ThrowIfNull(host);
 165
 1166        logger ??= NullLogger.Instance;
 167
 1168        var pluginFactory = host.Services.GetRequiredService<IPluginFactory>();
 1169        var candidateAssemblies = host.Services.GetRequiredService<IReadOnlyList<Assembly>>();
 170
 1171        RegisterHostPlugins(
 1172            host,
 1173            pluginFactory,
 1174            candidateAssemblies,
 1175            logger);
 176
 177        // Run post-build service collection plugins
 1178        var serviceProviderBuilder = host.Services.GetRequiredService<IServiceProviderBuilder>();
 1179        var configuration = host.Services.GetRequiredService<Microsoft.Extensions.Configuration.IConfiguration>();
 1180        serviceProviderBuilder.ConfigurePostBuildServiceCollectionPlugins(
 1181            host.Services,
 1182            configuration);
 183
 1184        return host;
 185    }
 186
 187    private static void RunHostApplicationBuilderPlugins(
 188        HostApplicationBuilder builder,
 189        IPluginFactory pluginFactory,
 190        IReadOnlyList<Assembly> candidateAssemblies,
 191        ILogger logger)
 192    {
 6193        logger.LogInformation("Configuring IHostApplicationBuilderPlugin plugins...");
 194
 6195        HostApplicationBuilderPluginOptions options = new(
 6196            builder,
 6197            candidateAssemblies,
 6198            logger,
 6199            pluginFactory);
 200
 12201        foreach (var plugin in pluginFactory.CreatePluginsFromAssemblies<IHostApplicationBuilderPlugin>(
 6202            candidateAssemblies))
 203        {
 0204            logger.LogInformation("Configuring host application builder plugin '{PluginName}'...", plugin.GetType().Name
 0205            plugin.Configure(options);
 206        }
 207
 6208        logger.LogInformation("Configured IHostApplicationBuilderPlugin plugins.");
 6209    }
 210
 211    private static void RegisterHostPlugins(
 212        IHost host,
 213        IPluginFactory pluginFactory,
 214        IReadOnlyList<Assembly> candidateAssemblies,
 215        ILogger logger)
 216    {
 1217        logger.LogInformation("Configuring IHostPlugin plugins...");
 218
 1219        HostPluginOptions options = new(
 1220            host,
 1221            candidateAssemblies,
 1222            pluginFactory);
 223
 2224        foreach (var plugin in pluginFactory.CreatePluginsFromAssemblies<IHostPlugin>(
 1225            candidateAssemblies))
 226        {
 0227            logger.LogInformation("Configuring host plugin '{PluginName}'...", plugin.GetType().Name);
 0228            plugin.Configure(options);
 229        }
 230
 1231        logger.LogInformation("Configured IHostPlugin plugins.");
 1232    }
 233}