Skip to content

Asynchronous DIContainer for Unity

License

MIT, Unknown licenses found

Licenses found

MIT
LICENSE
Unknown
LICENSE.meta
Notifications You must be signed in to change notification settings

mewlist/Doinject

Repository files navigation

Doinject

Asynchronous DI Container for Unity

Logo.svg

Table of Contents

Documentation

Build documentation

Example Project

Installation

Install via Unity Package Manager

Please install the packages in the following order:

https://github.com/mewlist/MewCore.git
https://github.com/mewlist/Doinject.git

About Doinject

Doinject is an asynchronous Dependency Injection (DI) framework for Unity.

It is built around the concept of an asynchronous DI container. Supports Unity 2022.3 LTS / Unity 6 and later.

Concepts

Asynchronous DI Containers

Traditional DI containers typically create instances synchronously. However, this approach doesn't easily support Unity's asynchronous operations, such as loading assets or fetching data before an object can be fully initialized.

Doinject provides a DI container designed for asynchronous workflows. It supports creating and disposing of instances through asynchronous processes. This allows for more flexible dependency management with a clear API, enabling scenarios like:

  • Instantiating objects after asynchronously loading their prefabs or assets via the Addressables system.
  • Initializing services with data fetched asynchronously from a network or file. Custom factories can be created to handle any asynchronous instantiation logic.

Context Scopes Aligned with Unity's Lifecycle

Doinject defines context scopes that naturally align with Unity's object lifecycle:

  • Project Context: Lives for the entire application duration.
  • Scene Context: Tied to a specific scene's lifetime. When the scene is unloaded, the context and its associated instances are disposed.
  • GameObject Context: Tied to a specific GameObject's lifetime. When the GameObject is destroyed, the context and its instances are disposed. These contexts automatically form parent-child relationships (e.g., Scene Context inherits from Project Context), allowing dependencies to be resolved hierarchically.

Integration with the Addressable Asset System

Doinject seamlessly integrates with Unity's Addressable Asset System. You can bind Addressable assets directly, and Doinject will automatically handle loading the asset asynchronously when needed and releasing its handle when the associated context is disposed. This simplifies resource management significantly compared to manual handle tracking.

Simplified Dependency Patterns

Doinject simplifies the implementation of common dependency management patterns:

  • Factory Pattern: Easily bind factories for creating instances on demand.
  • Singleton Pattern: Bind objects as singletons scoped to their context (Project, Scene, or GameObject).
  • Service Locator: While DI is generally preferred, Doinject can be used to manage globally or locally accessible services. Custom factories and resolvers offer further flexibility for complex instantiation logic.

Binding

Type Binding

Code Resolver Behavior Type
container.Bind<SomeClass>(); new SomeClass() cached
container.Bind<SomeClass>().AsSingleton(); new SomeClass() singleton
container.Bind<SomeClass>().AsTransient(); new SomeClass() transient
container.Bind<SomeClass>().Args(123,"ABC"); new SomeClass(123, "abc") cached
container.Bind<ISomeInterface>().To<SomeClass>(); new SomeClass() as ISomeInterface cached
container.Bind<ISomeInterface, SomeClass>(); new SomeClass() as ISomeInterface cached
container.Bind<SomeClass>()
.FromInstance(instance);
instance instance
container.BindInstance(instance); instance instance

Binding Lifetimes:

  • cached (Default): Creates an instance on the first resolution within its container and reuses that same instance for subsequent requests within that container. The instance is disposed (if IDisposable or IAsyncDisposable) when the container is disposed.
  • singleton: Creates a single instance within the context scope where it was first resolved. This instance persists for the lifetime of that context and is reused for all requests within that context and its child contexts. It is disposed when the context is disposed.
  • transient: Creates a new instance every time it is resolved. Doinject does not manage the lifecycle (creation/disposal) of transient instances beyond initial creation; manual disposal might be necessary.
  • instance: Binds a pre-existing instance to the container. Doinject does not manage the lifecycle (creation/disposal) of this instance.

MonoBehaviour Binding

Code Resolver Behavior
container.Bind<SomeComponent>(); new GameObject().AddComponent<SomeComponent>()
container
.Bind<SomeComponent>()
.Under(transform);
var instance = new GameObject().AddComponent<SomeComponent>();
instance.transform.SetParent(transform);
container
.Bind<SomeComponent>()
.On(gameObject);
gameObject.AddComponent<SomeComponent>()
container
.BindPrefab<SomeComponent>(somePrefab);
Instantiate(somePrefab).GetComponent<SomeComponent>()

Addressables Binding

Code Resolver Behavior
container
.BindAssetReference<SomeAddressableObject>(assetReference);
var handle = Addressables
.LoadAssetAsync<GameObject>(assetReference)

await handle.Task
container
.BindPrefabAssetReference<SomeComponent>(prefabAssetReference);
var handle = Addressables
.LoadAssetAsync<GameObject>(prefabAssetReference)

var prefab = await handle.Task

Instantiate(prefab).GetComponent<SomeComponent>()
container
.BindAssetRuntimeKey<SomeAddressableObject>("guid or path");
var handle = Addressables
.LoadAssetAsync<GameObject>("guid or path")

await handle.Task 
container
.BindPrefabAssetRuntimeKey<SomeComponent>("guid or path");
var handle = Addressables
.LoadAssetAsync<GameObject>("guid or path")

var prefab = await handle.Task

Instantiate(prefab).GetComponent<SomeComponent>()

Factory Binding

Code Resolver Behavior
container
.Bind<SomeClass>()
.AsFactory();
var resolver = new TypeResolver<SomeClass>()

new Factory<SomeClass>(resolver) as IFactory<SomeClass>
container
.Bind<SomeComponent>()
.AsFactory();
var resolver = new MonoBehaviourResolver<SomeComponent>()

new Factory<SomeComponent>(resolver))
as IFactory<SomeComponent>
container
.Bind<SomeClass>()
.AsCustomFactory<MyFactory>();
new CustomFactoryResolver<MyFactory>() as IFactory<SomeClass>

Factory bindings can also be combined with Addressables. For example, you can create a factory that asynchronously loads a prefab via Addressables, instantiates it, and returns a specific component:

container
  .BindAssetReference<SomeComponentOnAddressalbesPrefab>(assetReference)
  .AsFactory<SomeComponentOnAddressalbesPrefab>();
[Inject]
void Construct(IFactory<SomeComponentOnAddressalbesPrefab> factory)
{
  var instance = await factory.CreateAsync();
}

Injection

Installer

public class SomeInstaller : BindingInstallerScriptableObject
{
    public override void Install(DIContainer container, IContextArg contextArg)
    {
        container.Bind<SomeClass>();
    }
}

Constructor Injection

class ExampleClass
{
    // Constructor Injection
    public ExampleClass(SomeClass someClass)
    { ... }
}

Method Injection

class ExampleClass
{
    // Method Injection
    [Inject]
    public Construct(SomeClass someClass)
    { ... }
}

Injection into MonoBehaviours

To enable injection into a MonoBehaviour, it must implement the IInjectableComponent interface. Dependencies can then be injected via constructor (if applicable) or method injection.

using UnityEngine;
using Doinject;

// Inherit from MonoBehaviour and implement IInjectableComponent
public class ExampleComponent : MonoBehaviour, IInjectableComponent
{
    private SomeClass _someClassDependency;

    // Method Injection using [Inject] attribute
    [Inject]
    public void Construct(SomeClass someClass)
    {
        _someClassDependency = someClass;
        // ... use the dependency
    }
}

The container automatically finds and calls methods marked with [Inject] on components that implement IInjectableComponent within its context scope after the component is enabled.

About

Asynchronous DIContainer for Unity

Resources

License

MIT, Unknown licenses found

Licenses found

MIT
LICENSE
Unknown
LICENSE.meta

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •