Skip to content

gcsizmadia/EgonsoftHU.Extensions.DependencyInjection.Abstractions

Repository files navigation

Egonsoft.HU DependencyInjection Extensions Abstractions

GitHub Nuget Nuget

Abstractions for dependency injection.

Commonly Used Types:

  • EgonsoftHU.Extensions.DependencyInjection.IAssemblyRegistry
  • EgonsoftHU.Extensions.DependencyInjection.DefaultAssemblyRegistry

Table of Contents

Introduction

The motivation behind this project is to automatically discover and load all relevant assemblies into the current AppDomain.

Releases

You can download the package from nuget.org.

You can find the release notes here.

Instructions

First, install the EgonsoftHU.Extensions.DependencyInjection.Abstractions NuGet package.

dotnet add package EgonsoftHU.Extensions.DependencyInjection.Abstractions

Then, you may configure the logging of the DefaultAssemblyRegistry class to use the logging library you configured in your project.

Please note:

  • The internal dependency to Serilog has been removed from the package, hence no log event from DefaultAssemblyRegistry is logged by default.
  • It is optional to configure logging for DefaultAssemblyRegistry.
  • There are 2 forms of the message templates:
    • logEvent.MessageTemplate.Structured for use with structured logging, e.g. Serilog.
    • logEvent.MessageTemplate.Indexed for use with String.Format().

Example configuration for Serilog:

using EgonsoftHU.Extensions.DependencyInjection;
using EgonsoftHU.Extensions.Logging;

using Serilog;

ILogger logger = Log.Logger.ForContext<DefaultAssemblyRegistry>();

DefaultAssemblyRegistry.ConfigureLogging(
    logEvent =>
    logger
        .ForContext(PropertyBagEnricher.Create().AddRange(logEvent.Properties))
        .Verbose(logEvent.MessageTemplate.Structured, logEvent.Arguments)
);

Example configuration for Microsoft.Extensions.Logging:

using EgonsoftHU.Extensions.DependencyInjection;

using Microsoft.Extensions.Logging;

ILoggerFactory loggerFactory = LoggerFactory.Create(
    loggingBuilder =>
    loggingBuilder
        .SetMinimumLevel(LogLevel.Debug)
        .AddJsonConsole(
            options =>
            {
                options.IncludeScopes = true;
                options.JsonWriterOptions = new() { Indented = true };
            }
        )
        .AddDebug()
);

DefaultAssemblyRegistry.ConfigureLogging(
    logEvent =>
    {
        ILogger logger = loggerFactory.CreateLogger<DefaultAssemblyRegistry>();

        using (logger.BeginScope(logEvent.Properties))
        {
            logger.LogDebug(logEvent.MessageTemplate.Structured, logEvent.Arguments);
        }
    },
    LoggingLibrary.MicrosoftExtensionsLogging
);

Example configuration for System.Console:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using EgonsoftHU.Extensions.DependencyInjection;

DefaultAssemblyRegistry.ConfigureLogging(
    logEvent =>
    {
        var sb = new StringBuilder();

        sb.AppendLine();
        sb.AppendFormat(logEvent.MessageTemplate.Indexed, logEvent.Arguments);
        sb.AppendLine();

        int maxKeyLength = logEvent.Properties.Keys.Max(key => key.Length);

        foreach (KeyValuePair<string, object?> property in logEvent.Properties)
        {
            sb.AppendFormat(
                "  [{0}] = [{1}]",
                property.Key.PadRight(maxKeyLength),
                property.Value
            );

            sb.AppendLine();
        }

        Console.WriteLine(sb);
    }
);

Then, you can initialize an instance of the DefaultAssemblyRegistry class by providing the file name prefixes of your assemblies.

using EgonsoftHU.Extensions.DependencyInjection;

// This will search for assemblies as below then loads them into the current AppDomain:
//     Root Folder : AppContext.BaseDirectory
//     Pattern #1  : YourCompany.*.dll
//     Pattern #2  : Custom.*.dll
//     SearchOption: SearchOption.AllDirectories

DefaultAssemblyRegistry.Initialize("YourCompany", "Custom");

Finally, if you need the loaded assemblies then get the current instance of the DefaultAssemblyRegistry class.

using EgonsoftHU.Extensions.DependencyInjection;

var assemblies = DefaultAssemblyRegistry.Current.GetAssemblies();

Example

Suppose you have an ASP.NET Core project that needs to load controllers from other projects.

Let's create an extension method that will do the magic.

using System.Reflection;

using EgonsoftHU.Extensions.DependencyInjection;

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;

public static class MvcBuilderExtensions
{
    public static IMvcBuilder AddApplicationParts(this IMvcBuilder builder)
    {
        // This is the assembly of your ASP.NET Core startup project.
        // We will not call the AddApplicationPart() method for this assembly.
        Assembly entryAssembly = Assembly.GetEntryAssembly();

        DefaultAssemblyRegistry
            .Current
            .GetAssemblies()
            .Where(
                assembly =>
                assembly != entryAssembly
                &&
                assembly.DefinedTypes.Any(typeInfo => typeof(ControllerBase).IsAssignableFrom(typeInfo))
            )
            .ToList()
            .ForEach(assembly => builder.AddApplicationPart(assembly));

        return builder;
    }
}

Now you can use it in your Startup.cs file.

using EgonsoftHU.Extensions.DependencyInjection;

using Microsoft.Extensions.DependencyInjection;

public void ConfigureServices(IServiceCollection services)
{
    // Let's initialize the assembly registry.
    DefaultAssemblyRegistry.Initialize("YourCompany", "Custom");

    services
        .AddMvc()
        .AddApplicationParts(); // <-- This will load controllers, view components, or tag helpers from all assemblies other than the entry assembly.
}