> For the complete documentation index, see [llms.txt](https://docs.parsue.io/aceland-unity-packages/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.parsue.io/aceland-unity-packages/core-packages/injection/scopes-and-containers.md).

# Scopes & Containers

A **container** holds registrations and resolves instances. A **scope** is a child container that inherits from its parent but can add or override bindings and owns the lifetime of its own `Scoped` instances. This page explains the global container, Injection Scopes in the scene, and how to create scopes in plain code.

***

### The global container — `DI`

`DI` is the process-wide container. Packages publish services into it and anything can consume them without a reference to a scene object.

```csharp
using AceLand.Injection;

var score = DI.Resolve<IScoreService>();          // resolve
if (DI.TryResolve<IAudioService>(out var audio))  // safe resolve
    audio.Play("start");

DI.Inject(this);                                  // inject an existing object
var enemy = DI.CreateInstance<Enemy>();           // construct + inject a new object
```

<table data-search="false"><thead><tr><th width="261.4000244140625">Member</th><th>Purpose</th></tr></thead><tbody><tr><td><code>DI.Resolve&#x3C;T>(id)</code></td><td>Resolve a service (throws if missing).</td></tr><tr><td><code>DI.TryResolve&#x3C;T>(out T, id)</code></td><td>Resolve without throwing.</td></tr><tr><td><code>DI.Inject(obj)</code></td><td>Inject members/methods into an existing object.</td></tr><tr><td><code>DI.CreateInstance&#x3C;T>(args)</code></td><td>Construct an unregistered type and inject it.</td></tr><tr><td><code>DI.CreateScope(configure)</code></td><td>Open a child scope of the global container.</td></tr><tr><td><code>DI.IsGlobalBuilt</code></td><td>Whether the global container exists yet.</td></tr><tr><td><code>DI.StartupTask</code></td><td>Completes when global async entry points have started.</td></tr></tbody></table>

#### When is the global container built?

It is built automatically at `RuntimeInitializeLoadType.BeforeSceneLoad`, after every discovered global installer has run. You rarely build it yourself.

{% hint style="warning" %}
`DI.ConfigureGlobal(...)` must be called **before** the container is built — the recommended place is a `[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]`. Calling it after the container exists throws an `InjectionException`. For most bindings, prefer an `[AutoInstall]` installer instead (see [Registration & Lifetime](/aceland-unity-packages/core-packages/injection/registration-and-lifetime.md)).
{% endhint %}

***

### Injection Scope — the scene container

An **Injection Scope** is a MonoBehaviour that builds a child scope for a branch of the hierarchy. Add one via **GameObject ▸ AceLand ▸ Injection Scope**, then assign installers to it.

```mermaid
graph TD
    Global[DI.Global] --> Root[Scene Injection Scope]
    Root --> Sub[Child Injection Scope]
```

Key behaviours:

* It runs at `[DefaultExecutionOrder(-5000)]`, so it **builds and injects before any `Awake`**.
* Every MonoBehaviour under the scope's GameObject (and children not owned by a nested scope) is injected automatically — `[Inject]` and the component attributes are all filled in.
* A nested Injection Scope resolves its parent automatically, forming a parent→child chain that falls back to `DI.Global` at the root.

```csharp
using AceLand.Injection;

// Attach to the same GameObject as an Injection Scope (or reference as an asset).
public sealed class GameplayInstaller : MonoInstaller
{
    public override void Install(IContainerBuilder builder)
    {
        builder.Register<IEnemyFactory, EnemyFactory>(Lifetime.Scoped);
    }
}
```

{% hint style="info" %}
Because injection completes before `Awake`, you can safely use injected fields inside `Awake` and `Start`. Do **not** read them from a field initializer, which runs before injection.
{% endhint %}

***

### Creating scopes in code

Any resolver can open a child scope. The scope owns its `Scoped` and `Transient` instances and disposes them when you dispose the scope.

```csharp
using var scope = DI.CreateScope(builder =>
{
    // Extra bindings visible only inside this scope.
    builder.Register<IRequestContext, RequestContext>(Lifetime.Scoped);
});

var ctx = scope.Resolve<IRequestContext>();
// ... use ctx ...
// leaving the `using` block disposes the scope and its Scoped instances.
```

`IResolver` is the interface every container exposes:

<table data-search="false"><thead><tr><th width="325.4000244140625">Member</th><th>Purpose</th></tr></thead><tbody><tr><td><code>Resolve&#x3C;T>(id)</code> / <code>Resolve(Type, id)</code></td><td>Resolve a service.</td></tr><tr><td><code>TryResolve&#x3C;T>(out T, id)</code></td><td>Resolve without throwing.</td></tr><tr><td><code>CanResolve(Type, id)</code></td><td>Check whether a contract is resolvable.</td></tr><tr><td><code>Inject(obj)</code></td><td>Inject into an existing object.</td></tr><tr><td><code>CreateInstance&#x3C;T>(args)</code></td><td>Construct and inject an unregistered type.</td></tr><tr><td><code>CreateScope(configure)</code></td><td>Open a further child scope.</td></tr><tr><td><code>IsDisposed</code></td><td>Whether the container has been disposed.</td></tr></tbody></table>

***

### Disposal

Disposing a container disposes everything it owns, in reverse creation order:

* `IDisposable` instances get `Dispose()`.
* `IAsyncDisposable` instances get `DisposeAsync()`.
* Child scopes are disposed with their parent.
* `RegisterInstance(..., ownsInstance: true)` lets the container dispose an instance you supplied; the default (`false`) leaves ownership with you.

The global container is disposed automatically on `Application.quitting`; you can force it with `DI.DisposeGlobal()`.

***

### Best Practices

* Publish shared services in the **global** container; keep per-scene state in an **Injection Scope**.
* Read injected fields in `Awake`/`Start`, never in field initializers.
* Wrap code-created scopes in `using` so their `Scoped` instances are released deterministically.
* Let scopes fall through to their parent — only re-register a contract in a child when you truly need to override it.
