J4JSoftware.DeusEx 1.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package J4JSoftware.DeusEx --version 1.1.0                
NuGet\Install-Package J4JSoftware.DeusEx -Version 1.1.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="J4JSoftware.DeusEx" Version="1.1.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add J4JSoftware.DeusEx --version 1.1.0                
#r "nuget: J4JSoftware.DeusEx, 1.1.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install J4JSoftware.DeusEx as a Cake Addin
#addin nuget:?package=J4JSoftware.DeusEx&version=1.1.0

// Install J4JSoftware.DeusEx as a Cake Tool
#tool nuget:?package=J4JSoftware.DeusEx&version=1.1.0                

J4JSoftware.DeusEx

The library repository is available on github.

The change log is available here.

J4JDeusEx is a simple ViewModelLocator-type object which makes dependency injection available within frameworks which don't natively support it via a static ServiceProvider property. Classes derived from J4JDeusEx integrate with my J4JHostConfiguration library to integrate that service provider capability with app configuration.

J4JDeusEx also provides access to logging via my ILogger library, both during program startup/initialization and execution. Log messages generated by the host configuration and building process are also automatically applied to the main logging facility once it has been started (normal logging is not available during the host configuration and building phase because that's when normal logging gets initialized). Finally, it also writes crash messages that occur very early in the configuration process to a crash file.

For details on J4JHostConfiguration please refer to its GitHub documentation.

For details on J4JLogging please refer to its GitHub documentation.

Background

To get the most out of dependency injection you need to have a "root" resolver, a component which gives you access to the automagic creation that dependency injection enables.

In many modern frameworks this functionality is built-in. You don't need to worry about it, you just request objects (usually via constructor injection) and everything just works.

For older frameworks or frameworks built on older frameworks -- WPF, Windows Applications v3 in its current iteration, console apps, etc. -- that built-in functionality doesn't exist. It needs to be made explicitly available. Hence things like the ViewModelLocator pattern.

J4JDeusEx is an approach I developed to integrate this functionality to the IHostBuilder/IHost API.

The name J4JDeusEx, by the way, is a play on the phrase "deus ex machina", literally "god in the machine", an approach lazy story writers use to work their way out of complex plot traps: you just invoke some miraculous power or event and, voila!, the story is suddenly reasonable again.

How It Works

In order to be universally available J4JDeusEx is accessed through static properties and methods. For example, to create an instance of a service you've defined via dependency injection you'd do something like this:

var fooBar = J4JDeusEx.ServiceProvider.GetRequiredService<IFooBar>();

ServiceProvider is not nullable...so if something goes wrong in setting up J4JDeusEx, you'll get a J4JDeuxExException (and perhaps a log message as well, depending on what state J4JDeusEx is in).

But how is J4JDeusEx configured? How does it have access to the underlying IServiceProvider it returns?

J4JDeusExHosted

The answer is it's initialized from within derived classes which set the static ServiceProvider property via a protected setter.

How that's done depends on how you handle your app's startup. Since I wrote my J4JHostConfiguration API to handle the way I like to start up my apps there's a derived class, J4JDeusExHosted, which works closely with the J4JHostConfiguration system.

J4JDeusExHosted is a relatively simple abstract class:

public abstract class J4JDeusExHosted : J4JDeusEx
{
    protected abstract J4JHostConfiguration? GetHostConfiguration();

    protected virtual string GetCrashFilePath( 
        J4JHostConfiguration hostConfig, 
        string crashFileName = "crashFile.txt" )
    {
        var fileName = Path.GetFileName( crashFileName );

        if( string.IsNullOrEmpty( fileName ) )
            fileName = "crashFile.txt";

        return Path.Combine( hostConfig.ApplicationConfigurationFolder, fileName );
    }

    public bool Initialize( 
        string crashFileName = "crashFile.txt" )
    {
        var hostConfig = GetHostConfiguration();

        if( hostConfig == null )
            throw new J4JDeusExException( $"Undefined {typeof( J4JHostConfiguration )}" );

        CrashFilePath = GetCrashFilePath( hostConfig, crashFileName );

        if (hostConfig.MissingRequirements != J4JHostRequirements.AllMet)
        {
            OutputFatalMessage(
                $"Missing {typeof( J4JHostConfiguration )} items: {hostConfig.MissingRequirements}",
                hostConfig.Logger );

            return false;
        }

        Logger = hostConfig.Logger;

        var host = hostConfig.Build();

        if( host != null )
        {
            ServiceProvider = host.Services;

            var runTimeLogger = host.Services.GetService<ILogger>();
            runTimeLogger?.OutputCache( hostConfig.Logger );

            Logger = runTimeLogger;

            IsInitialized = true;

            return true;
        }

        Logger = null;

        OutputFatalMessage( $"Could not create {typeof( IJ4JHost )}",
                            hostConfig.Logger );

        return false;
    }
}

It has one abstract protected method you must override, GetHostConfiguration(), to retrieve an instance of a J4JHostConfiguration object which it builds in order to get the IServiceProvider object it needs. J4JDeusEx is initialized -- it's static properties are set -- by calling J4JDeusExHosted.Initialize().

If something goes wrong with the initialization process the following happens:

  • a fatal error message is output to a crash dump file located in the application configuration folder you specified when you configured the J4JConfigurationBuilder object; and,
  • the static IsInitialized will be set to false.

If the initializataion is successful several things will happen:

  • a runtime instance of ILogger will be created if one was defined in the J4JConfigurationBuilder object and assigned to the static Logger property;
  • the static ServiceProvider property will be set; and,
  • the static IsInitialized will be set to true.

J4JDeusExWinApp

Because Windows Apps (aka Win3 apps) are sandboxed, the crash file location needs to be different than that of a non-sandboxed app. The derived abstract class J4JDeusExWinApp in my J4JSoftware.WindowsAppUtilities library handles the details. It's an abstract class because the GetHostConfiguration() must still be overridden to supply the J4JHostConfiguration instance for your app.

Example: Console App

Here's a complete example of configuring and initializing J4JDeuxEx in a console app. First, derive a class from J4JDeusExHosted to supply the J4JHostConfiguration object associated with your app:

internal partial class DeusEx : J4JDeusExHosted
{
    protected override J4JHostConfiguration? GetHostConfiguration()
    {
        var hostConfig = new J4JHostConfiguration( AppEnvironment.Console )
                        .ApplicationName( "WpFormsSurveyProcessor" )
                        .Publisher( "Jump for Joy Software" )
                        .LoggerInitializer( ConfigureLogging )
                        .AddDependencyInjectionInitializers( ConfigureDependencyInjection )
                        .FilePathTrimmer( FilePathTrimmer );

        var cmdLineConfig = hostConfig.AddCommandLineProcessing( CommandLineOperatingSystems.Windows )
                                      .OptionsInitializer( SetCommandLineConfiguration )
                                      .ConfigurationFileKeys( true, false, "c", "config" );

        return hostConfig;
    }

    // details on configuring logging, dependency injection and the command line omitted
    // for clarity
}

The details of how the J4JHostConfiguration object is configured depends on what capabilities you want in your app. In this example I've:

  • set up logging (using ILogger, and configured through the ConfigureLogger() method which isn't shown);
  • set up my dependency injection environment (through the ConfigureDependencyInjection() method which isn't shown);
  • arranged to trim file paths when outputting log events with source code annotations; and,
  • configured command line parsing (through the SetCommandLineConfiguration() method which isn't shown)

You can read more about how to set up those configuration methods elsewhere in this documentation.

Having defined my local DeusEx class I use it in my program startup code:

internal class Program
{
    static void Main( string[] args )
    {
        var deusEx = new DeusEx();

        if( !deusEx.Initialize() )
        {
            J4JDeusEx.Logger?.Fatal("Could not initialize application");
            Environment.ExitCode = 1;
            return;
        }

        // remaining code not shown for clarity

Once my locally-defined derived class is instantiated I can refer to the static methods and properties of J4JDeusEx anywhere in my codebase.

Example: Windows Application

The only difference when using the J4JDeusEx system within a Windows App (aka Win3 app) is that you derive your local deus ex class from J4JDeusExWinApp. Everything else regarding using J4JHostConfiguration remains the same.

Product Compatible and additional computed target framework versions.
.NET net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on J4JSoftware.DeusEx:

Package Downloads
J4JSoftware.DependencyInjection

provides customized IHostBuilder and IHost classes for simplified configuration

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.1.1 267 4/11/2023
1.1.0 212 4/4/2023
1.0.0 342 12/27/2022

migrated logging from Serilog to Microsoft