Open fckaye opened 2 months ago
The problem here is that you do not register BarController
in your GameLifetimeScope
, then VContainer does not know how to find a BarController
for you. entryPoints.Add<BarController>();
only does two things:
BarController
is a IStartable
(and other interfaces it implements), you can also do that by builder.Register<BarController>(Lifetime.Singleton).AsImplementedInterfaces()
, now, everytime you require a IStartable
, VContainer will throw you an instance of BarController
.EntryPointDispatcher
is registered, a class who is responsible for calling IStartable.Start()
Please note here, entryPoints.Add<BarController>();
does not tell VContainer what to do when someone needs a BarController
, to tell VContainer that, you need the following code:
entryPoints.Add<BarController>().AsSelf();
But here is another problem, your BarController
requires FooController
too, this creates a circular reference.
When VContainer create your FooController
, it must create a BarController
first, otherwise it has no argument for FooController
's constructor. But when creating BarController
, VContainer must find a FooController
too. which creates a dead loop.
To solve this problem, you can remove the dependency of FooController
from BarController
's constructor, instead setting BarController.fooController
at the end of FooController
's constructor.
Please note, this is not the best practice, you should avoid circular dependency.
Here is all the code that changed:
public class GameLifetimeScope : LifetimeScope
{
[SerializeField] private FooView fooView;
[SerializeField] private BarView barView;
protected override void Configure(IContainerBuilder builder)
{
builder.RegisterComponent(fooView);
builder.RegisterComponent(barView);
builder.UseEntryPoints(Lifetime.Singleton, entryPoints =>
{
entryPoints.Add<FooController>().AsSelf();
entryPoints.Add<BarController>().AsSelf();
});
}
}
public class FooController : IStartable
{
private readonly FooView fooView;
private readonly BarController barController;
public FooController(FooView fooView, BarController barController)
{
this.fooView = fooView;
this.barController = barController;
this.barController.fooController = this;
}
void IStartable.Start()
{
fooView.FooButton.onClick.AddListener(() => barController.UpdateLabelText("Bullshit coming from FooController"));
}
public void UpdateLabelText(string contents)
{
fooView.FooLabel.text = $"FooLabel: \n{contents}";
}
}
public class BarController : IStartable
{
private readonly BarView barView;
public FooController fooController;
public BarController(BarView barView)
{
this.barView = barView;
}
void IStartable.Start()
{
barView.BarButton.onClick.AddListener(() => fooController.UpdateLabelText("Bullshit coming from BarController"));
}
public void UpdateLabelText(string contents)
{
barView.BarLabel.text = $"BarLabel: \n{contents}";
}
}
I am quite new to this Dependency Injection thing in general, so I'm sorry if the question is silly or if there is a workaround that I am not aware of.
I was trying to follow the HelloWorld example on the documentation and start using VContainer from there.
Then I had a problem, when I was registering 2 classes which implement
IStartable
and one has a reference to another. I would get a message like the following:I tried to reduce my app to a minimal setup so my trouble would be easier to understand/reproduce.
Imagine we have the following 5 scripts:
BarController
It basically takes BarView Button and OnClick, it will change the text contents of the Label of FooView through FooController
BarView
FooController
It basically takes FooView Button and OnClick, it will change the text contents of the Label of BarView through BarController
FooView
GameLifetimeScope
LifetimeScope to register components
Wiring up all the references on UnityEditor and pressing play will result in the following VContainerException:
I would really appreciate any help!