Injecter.Unity 
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
MonoInjectorto theGameObjecthosting the script MonoInjectorruns atAwake, and it's execution order (int.MinValue- first) is run before your own component'sAwake. Every injected script will have it's ownIServiceScopederived from the root scope. This scope can be retrieved through theIScopeStore, and the owner of the scope is the script being injected- When the
GameObjectis destroyed,MonoDisposerwill run during theOnDestroymethod, with an execution order ofint.MaxValue- last
- Add
- Or derive from
MonoBehaviourInjecter- The base class
Awakemethod will do the injections, and theOnDestroymethod will dispose of the scope
- The base class
- Either use the helper components
Getting started
Install dependencies
- Install
InjecterandMicrosoft.Extensions.DependencyInjectionthrough UnityNuget. - Install
Injecter.Unitythrough 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:
- Set up a composition root as described above.
- Remove inheriting from the old
MonoBehaviourInjectedand similar classes - Optional - Decorate your injected scripts with
[RequireComponent(typeof(MonoInjector))] - In the editor press the
Tools / Injecter / Ensure injection scripts on everytingbutton - If a
GameObjectis missing theMonoInjectororMonoDisposerscripts, add them