Search code examples
c#unity-game-enginezenject

Layered architecture by Zenject's DiContainers


In my application I have three layers: * Data (Entities and Data Access Objects) * Model (Managers) * Presentation (Views)

image

I decided that SubContainers is my choice.

In the root GameInstaller I create every container and manually install them with LayerInstallers:

public class GameInstaller : MonoInstaller
{

    public GameDataLayerInstaller DataLayerInstaller;
    public GameModelLayerInstaller ModelLayerInstaller;
    public GamePresentationLayerInstaller PresentationLayerInstaller;

    public override void InstallBindings()
    {
        var dataContainer = Container.CreateSubContainer();
        dataContainer.Inject(DataLayerInstaller);
        DataLayerInstaller.InstallBindings();

        var modelContainer = dataContainer.CreateSubContainer();
        modelContainer.Inject(ModelLayerInstaller);
        ModelLayerInstaller.InstallBindings();

        var presentationContainer = modelContainer.CreateSubContainer();
        presentationContainer.Inject(PresentationLayerInstaller);
        PresentationLayerInstaller.InstallBindings();
    }
}

Inside of Model-Installer I add GameAppManager into the graph.

public class GameModelLayerInstaller : MonoInstaller
{

    public GameAppManager GameAppManager;

    public override void InstallBindings()
    {
        Container.BindInstance(GameAppManager);
        Container.QueueForInject(GameAppManager);
    }
}

Inside of Presentation-Installer I add GameApp into the graph.

public class GamePresentationLayerInstaller : MonoInstaller
{

    public GameApp GameApp;

    public override void InstallBindings()
    {
        Container.BindInstance(GameApp);
        Container.QueueForInject(GameApp);
    }
}

Then I trying to resolve it from GameApp.InjectDependencies(...) method:

public class GameApp : MonoBehaviour
{

    private GameAppManager _appManager;
    [Inject]
    public void InjectDependencies(GameAppManager appManager)
    {
        _appManager = appManager;
    }
}

But Zenject throws this exception:

ZenjectException: Unable to resolve type 'RsQuest.Game.Model.App.GameAppManager' while building object with type 'RsQuest.Game.Presentation.App.GameApp'. 
Object graph:
GameApp

How can I manage this case? Is there any better way with GameObjectContext?


Solution

  • My best guess for what's happening is GameApp is being injected twice. Once by by presentation container, and once by the SceneContext container. By default, any MonoBehaviours in the scene are assumed to belong to the SceneContext unless they are inside a GameObjectContext. So you're right, I think the best way to do what you're attempting to do is to use GameObjectContext. Usually you shouldn't need to ever call DiContainer.CreateSubContainer yourself.

    If you've already got MonoBehaviours in the scene at startup that you want to be inside a subcontainer, then you can directly add a GameObjectContext parent transform for these objects. Or, if they are created dynamically, then you can use one of the FromSubContainerResolve.ByNewPrefabX methods, either as a factory or directly on the scene container.