gustavopsantos / Reflex

Minimal dependency injection framework for Unity
MIT License
730 stars 51 forks source link

Is it possible to inherit scope from parent scene scope when LoadScene? #53

Closed siehc closed 5 months ago

siehc commented 5 months ago

Hi Is it possible to inherit scope from the parent scene instead of the root scope? This would allow me to avoid placing too many singleton objects in the root scope.

gustavopsantos commented 5 months ago

Hello @siehc, I dont think I understood you question. Reflex create one root ProjectContainer and then scopes it for every new opened scene like the following schema:

graph TD;
    ProjectContainer-->SceneOneContainer;
    ProjectContainer-->SceneTwoContainer;
    ProjectContainer-->SceneThreeContainer;

With this schema in mind, you wanted to create a scope from which container? From a scene container or from root ProjectContainer?

siehc commented 5 months ago

If SceneTwo is opened by SceneOne I want SceneTwoContainer to inherit from SceneOneContainer instead of ProjectContainer

gustavopsantos commented 5 months ago

@siehc No, thats not possible, and also I do not recommend. Unity scenes implementation theres no hierarchy, so theres no guarantee that, for instance, scene two, opened by scene one, will be disposed once scene one is disposed. As scenes are a flat hierarchy, not a tree. Howerver what most people do, even I, is to pre inject a scene using ReflexSceneManager.PreInstallScene with what you need. Most people used it for creating boot scenes, a scene that prepare all needed services for a complete session scene. You can see a simple example of that in https://github.com/gustavopsantos/Reflex?tab=readme-ov-file#-getting-started, at step 18.

siehc commented 5 months ago

But singletons must be prepared in the ProjectContainer instead of the BootScene, otherwise they cannot be shared across scenes, right?

siehc commented 5 months ago
graph TD;
    ProjectContainer-->SceneLoginContainer;
    SceneLoginContainer-->SceneLobbyContainer;
    SceneLobbyContainer-->SceneShopContainer;
    SceneLobbyContainer-->SceneSettingContainer;

I think there are some pros here.

  1. No need to maintain many PreInstallScene. Most objects or services injected by DI are used by more than one scene. If scene scope could inherit from parent scene scope, then we can maintain minimal PreInstallScene. In my case, most objects and services could just be prepared in SceneLobbyContainer.

  2. Easy to clean up singletons. For instance, in a logout scenario, it's easier to clean up when most objects and services that need to be cleared are prepared in SceneLobbyContainer. You just close all scenes and return to SceneLogin. However, if objects or services are prepared by ProjectContainer, we have to clear them manually.

    Although I can maintain the scene hierarchy by myself, the lack of inheritance from parent scopes makes it less enjoyable to use Reflex. :persevere:

gustavopsantos commented 5 months ago

But singletons must be prepared in the ProjectContainer instead of the BootScene, otherwise they cannot be shared across scenes, right?

If a singleton object is constructed at SceneLogin scene and injected to SceneLobbyContainer using PreInstallScene, and then you scope SceneShopContainer from SceneLobbyContainer, the singleton object will be only disposed when the owner container is disposed (SceneLobbyContainer). In another words, inherited bindings are never disposed by the container, only self/owned bindings, allowing for children containers to be disposed without affecting parent life cycle.

gustavopsantos commented 5 months ago

@siehc One thing you can do (havent tested but I think it can be done) is to create a method that opens a scene "from another" scene, forcing this container hierarchy you want. You will have to use PreInstallScene functionality and also reflection to get container.ResolversByContract but since its a property, you can reflect it once and create a GetDelegate for really fast get invokes.

gustavopsantos commented 5 months ago

@siehc What you can also do is to change reflex internals to your needs, check UnityInjector::CreateSceneContainer on how scene containers are created, here you can change from which container the scene container are being scoped. I'll be also thinking in how reflex can be updated to safely allow scene container inheritance as a opt-in change, so it does not break for other users if I decide to push it.

gustavopsantos commented 5 months ago

@siehc Just to let you know, Im working in a solution do allow you to control scene parent containers as a opt-in feature. Its already working however I need to do some more tests to ensure everything else kept working as it was.

gustavopsantos commented 5 months ago

@siehc See lastest release, it has the feature you need, https://github.com/gustavopsantos/Reflex/releases/tag/8.5.0