ssannandeji / Zenject-2019

Dependency Injection Framework for Unity3D
MIT License
2.53k stars 366 forks source link

PlaceholderFactory 'Create' with extra parameter via FromComponentInNewPrefab Validation Failure #646

Closed spreagtha closed 5 years ago

spreagtha commented 5 years ago

Hello,

When attempting to use PlaceholderFactory Create method with an extra parameter, the parameter is not being bound and fails validation with the following error:

ZenjectException: Unable to resolve 'bool' while building object with type 'FactoryTest'. Object graph:
FactoryTest

The following code is a simplification of the situation in my game project's code which fails the validation check for a parameter that should be injected through the PlaceholderFactory Create method. The instantiated prefab is of the 'FactoryTest' type as in my example code, and will also spawn instances of it's own prefab recursively in certain scenarios (my use case was a 'state tree node' type which recursively spawns on certain nodes.

FactoryTestObjectInstaller.cs

using UnityEngine;
using Zenject;

namespace ZenjectTest
{

public class FactoryTestObjectInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        Container.Bind<Transform>().FromComponentSibling();
    }
}

} //namespace ZenjectTest

FactoryTest.cs

using UnityEngine;
using Zenject;

namespace ZenjectTest
{

public class FactoryTest : MonoBehaviour
{

    public bool StartOn;

    FactoryTest.Factory _Factory;

    public bool First = true;

    [Inject]
    private void Construct(Transform flowPlayer, FactoryTest.Factory factory, bool startOn)
    {
        StartOn = startOn;
        _Factory = factory;
    }

    private void Start()
    {
        if (First)
        {
            var created = _Factory.Create(true);
            created.First = false;
        }
    }

    public class Factory : PlaceholderFactory<bool, FactoryTest>
    {
    }
}

} //namespace ZenjectTest

TestInstaller.cs

using Zenject;

namespace ZenjectTest
{

public class TestInstaller : MonoInstaller
{

    public FactoryTest TestPrefab;

    public override void InstallBindings()
    {
        Container.BindFactory<bool, FactoryTest, FactoryTest.Factory>().FromComponentInNewPrefab(TestPrefab);
    }
}

} //namespace ZenjectTest

FactoryUseTest.cs

using UnityEngine;
using Zenject;

namespace ZenjectTest
{

public class FactoryUseTest : MonoBehaviour
{

    FactoryTest.Factory Factory;

    [Inject]
    private void Construct(FactoryTest.Factory factory)
    {
        Factory = factory;
    }

    private void Awake()
    {
        Factory.Create(true);
    }
}

} //namespace ZenjectTest

The prefab 'FactoryTest' script is on FactoryTestPrefab

The 'root' FactoryTest spawning script FactoryUseTest

The SceneContext SceneContext

The Scene hierarchy ZenjectTestSceneHierarchy

spreagtha commented 5 years ago

After reading SubContainers.md, it appears this is expected behaviour as the passed parameters are injected into the sub-containers installer, and not directly into the facade.

Per SubContainers.md

One important difference with creating a Sub-Container using a factory, is that the parameters you supply to the factory are not necessarily forwarded to the facade class. In this example, the parameter is a float value for speed, which we want to forward to the ShipInputHandler class instead. That is why these parameters are always forwarded to an installer for the sub-container, so that you can decide for yourself at install time what to do with the parameter. Another reason for this is that in some cases the parameter might be used to choose different bindings.