Show / Hide Table of Contents

Injecter.Unity openupm

Since version 3.0.1 you need to provide the following dlls yourself:

  • Injecter
  • Microsoft.Extensions.DependencyInjection
  • Microsoft.Extensions.DependencyInjection.Abstractions
Note

The recommended way of installing NuGet packages is through the UnityNuget project

Fundamentals

The Injecter.Unity lets you set up the following flow:

  • A "composition root" is initialized part of the entry point of the application
  • Create a script which needs to be injected
  • Choose an injection method:
    • Either use the helper components
      • Add MonoInjector to the GameObject hosting the script
      • MonoInjector runs at Awake, and it's execution order (int.MinValue - first) is run before your own component's Awake. Every injected script will have it's own IServiceScope derived from the root scope. This scope can be retrieved through the IScopeStore, and the owner of the scope is the script being injected
      • When the GameObject is destroyed, MonoDisposer will run during the OnDestroy method, with an execution order of int.MaxValue - last
    • Or derive from MonoBehaviourInjecter
      • The base class Awake method will do the injections, and the OnDestroy method will dispose of the scope

Getting started

Install dependencies

  1. Install Injecter and Microsoft.Extensions.DependencyInjection through UnityNuget.
  2. Install Injecter.Unity through openupm
openupm add com.injecter.unity

Setup root

Either create manually, or through the Assets / Injecter editor menu, create your composition root.

#nullable enable
using Injecter;
using Microsoft.Extensions.DependencyInjection;
using UnityEngine;

public static class AppInstaller
{
    /// <summary>
    /// Set this from test assembly to disable
    /// </summary>
    public static bool Run { get; set; } = true;

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
    public static void Install()
    {
        if (!Run) return;

        var serviceProvider = new ServiceCollection()
            .Configure()
            .BuildServiceProvider(true);

        // Injected scripts will get the root service provider from this instance
        CompositionRoot.ServiceProvider = serviceProvider;

        Application.quitting += OnQuitting;

        /// <summary>
        /// Will dispose of all services when quitting
        /// </summary>
        async void OnQuitting()
        {
            Application.quitting -= OnQuitting;

            await serviceProvider.DisposeAsync().ConfigureAwait(false);
        }
    }

    public static IServiceCollection Configure(this IServiceCollection services)
    {
        services.AddInjecter(o => o.UseCaching = true);
        // TODO: Add services

        return services;
    }
}

Inject using the helper components

Inject into MonoBehaviours

Create a script which will receive injection

[RequireComponent(typeof(MonoInjector))]
public class MyScript : MonoBehaviour
{
    [Inject] private readonly IMyService _service = default!;
}

Add MonoInjector

If you decorate your script with [RequireComponent(typeof(MonoInjector))] then, when adding the script to a GameObject the editor will add the MonoInjector and the MonoDisposer script to your GameObject. If for some reason this does not happen (for example, when changing an already living script into one needing injection), either add the MonoInjector component manually, or use the editor tools included to add the missing components to scenes or prefabs (will search all instances) through the editor menu Tools / Injecter / ...

Inject by inheriting from MonoBehaviourInjected

Create a script which will receive injection

public class MyScript : MonoBehaviourInjected
{
    [Inject] private readonly IMyService _service = default!;

    protected override void Awake()
    {
        base.Awake();

        // Custom logic goes here
    }

    protected override void OnDestroy()
    {
        // Custom cleanup logic goes herer

        base.OnDestroy();
    }
}

Manual injection

When dynamically adding an injectable script to a GameObject, you should not annotate the injected script with the RequireComponent attribute, and add the MonoInjector manually, like so:

var myObject = new GameObject("MyObject");
myObject.AddComponent<TestComponent>();
myObject.AddComponent<MonoInjector>();

public class MyComponent : MonoBehaviour
{
    [Inject] private readonly MyService _service = default!;
}

Testing

You can load tests and prefabs with controlled dependencies when running tests inside Unity. To do this create the following class in your test assembly:

public static class InstallerStopper
{
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
    public static void DisableAppInstaller()
    {
        if (Environment.GetCommandLineArgs().Contains("-runTests")
            || EditorWindow.HasOpenInstances<TestRunnerWindow>())
        {
            AppInstaller.Run = false;
        }
    }
}

This will stop your AppInstaller from running when you execute tests. This will happen if either you have the test runner window open, or you are running Unity headless with the -runTests parameter (typically during CI).

Warning

If you do this, then you must close the test runner window when entering play mode, otherwise the AppInstaller will not run

In your tests set up the composition root manually

[UnityTest]
public IEnumerator My_Test_Does_Stuff()
{
    CompositionRoot.ServiceProvider = new ServiceCollection()
        .AddTransient<IService, MyTestService>()
        .BuildServiceProvider();

    // Do your tests, load scenes, prefabs, asserts etc...

    CompositionRoot.ServiceProvider.Dispose();
    (CompositionRoot.ServiceProvider as IDisposable)?.Dispose();
};
Note

You can also do the same in the test's Setup or Teardown stage

Migrating from 8.0.1 to 9.0.0 and above

If you want to continue using the inheritance setup, then change all base classes to be MonoBehaviourInjected. The new base class will always create scopes.

If you want to migrate to the component-based injection:

  1. Set up a composition root as described above.
  2. Remove inheriting from the old MonoBehaviourInjected and similar classes
  3. Optional - Decorate your injected scripts with [RequireComponent(typeof(MonoInjector))]
  4. In the editor press the Tools / Injecter / Ensure injection scripts on everyting button
  5. If a GameObject is missing the MonoInjector or MonoDisposer scripts, add them
  • Improve this Doc
In This Article
Back to top Generated by DocFX