Svelto Inversion of Control Container

If it’s the first time you visit my blog, please don’t forget to read my main articles on the subject before to continue this post:

It’s finally time to share the latest version of my Inversion of Control container, which I named Svelto IoC, which I will keep updated from now on. This new version is the current IoC container that Freejam is using for the Unity3D game Robocraft (http://www.robocraftgame.com).

Thanks to the possibility to use the library in production, I could analyse in depth the benefits and disadvantages in using extensively an IoC container on a big project with a medium sized team of programmers. I am preparing an exhaustive article on the subject, but I am not sure when I will be able to publish it, so stay tuned.

The new IoC container is structurally similar to the old one, but has several major differences. In order to use it, a UnityRoot specialised monobehaviour must still be created. The class that implements ICompositionRoot is the Compositon Root of the project. The game object holding the UnityRoot monobehaviour is the game context of the scene.

All the dependencies bound in the Composition Root, will be injected during the Unity Awake period. Dependencies cannot be used until the OnDependenciesInjected function or the Start function (in case of dependencies injected inside monobehaviours) are called. Be careful though, OnDependenciesInjected is called while the injection waterfall is still happening, however injected dependencies are guaranteed to have, on their turn, their dependencies injected. Dependencies are not guaranteed to be injected during the Awake period, therefore you shouldn’t use injected dependencies inside Monobehaviour Awake calls.

Other changes include:

  • Monobehaviours that are created by unity after the scene is loaded, don’t need to ask explicitly to fill the dependencies anymore. They will be automatically injected.
  • Monobehaviours cannot be injected as dependency anymore (that was a bad idea).
  • Dynamically created monobehaviours have always dependencies injected through factories (MonoBehaviourFactory and GameObjectFactory are part of the framework).
  • Now all the contracts are always injected through “providers”, this simplifies the code and makes it more solid. Also highlights the importance of providers in this framework.
  • A type injection cache has been developed, therefore injecting dependencies of the same type is way faster than it used to be.
  • It’s now possible to create new instances for each dependency injected, if the factory MultiProvider is used explicitly.
  • You can create your own provider for special cases.
  • Improved type safety of the code. It’s not possible anymore to bind contracts to wrong types. For this reason AsSingle() has been substituted by BindSelf().
  • Various improvements and bug fixes.
  • Dependencies can be injected as weak references automatically.

What is still need to do:

  • Improve the documentation and explain the bad practices
  • Add the possibility to create hierarchical context and explain why they are necessary
  • Add the possibility to inject dependencies by construction, in order to reduce the necessity to hold references.
  • Explain how to exploit custom providers.

The new project can be found at this link: https://github.com/sebas77/Svelto-IoC

 

21 thoughts on “Svelto Inversion of Control Container

  1. Thanks for sharing the newest version of the container!

    After reading the post, I was wondering why you said that injecting into MonoBehaviours was a bad idea. Since the very first version of your container, I always enjoyed this concept and I even carried it on to my current container, Adic. With a little bit of caching, this has never been a problem in any game I implemented it.

    Why did you changed your mind about it?

    1. I have ready a very long article that encloses all my current thoughts about code design which I will publish soon, even if I didn’t finish to code the proof of concept (it will take too much time otherwise). In it, you will find more details about my current decisions.

      According my current rules Monobehaviours don’t ever need to be injected (it’s a feature that is not needed, therefore better remove it for simplicity). Monobehaviours in my current vision can be used as

      – behaviours for game objects, therefore they don’t need to be injected anywhere, however they can have dependencies injected
      – view for MVP pattern, even in this case they don’t need to be injected anywhere.
      – data source, in this case they are used in the service layer just as…data source. Again they don’t need to be injected anywhere.

      P.S.: it was also a bad idea because if the MB is destroyed, it can be referenced somewhere else than the Gameobject itself, which is dangerous.

      1. Interesting thoughts on the problem!

        However, after reading your P.S., it made me think that maybe I understood your observation all wrong. In fact, injecting MonoBehaviours somewhere is really a bad idea, as you pointed out – missing references can be a mess! However, as you also indicated, injecting dependencies INTO MonoBehaviours is no problem at all – and that’s where I think got it all wrong: I was thinking that wiring dependencies into MonoBehaviours was a bad idea.

        Anyway, your observations were interesting, as usual. I’ll wait until the article to read more details about your decisions on the framework.

        Thanks for the reply!

  2. I like a lot your tight implementation and minimalistic approach.
    One question though – why no container hierarchies?
    Simple parent-children hierarchy would be quite easy to add into your framework.
    Have you never meet the need?

    1. Thanks, I was talking about it with my colleague today. So far I didn’t find a reason to use it, but actually I lately realised that probably the reason why coders tend to use injection as singletons is mainly due to the injection blob resulting by using one container only.
      I may add the hierarchical container feature soon.

      1. My most frequent use case is kind of builder/factory that uses child container to wiry dependencies for a complex object assembled from several components. Where parent container provides access to the global (often singleton) services.

        For my experiments, I’ve just added a simple single-parent container support into your framework – like 6 new lines or so.
        I think its worth it.

        (but now I see a need to have nested lookup contexts too… no, better to stop right now. 😉

          1. Please find below an example code of my usage of the hierarchical containers.

            The idea is simple: root container defines all “globally” accessible services (app-wide singletons),
            while children containers are used to assist in wiring together of the “complex” objects – e.g. Vehicles in my example.

            VehicleBuilder class defines a child container with all necessary components required to construct a new Vehicle instance.
            For simplicity, the container is discarded after the vehicle is constructed.
            (some kind of nested scope support could help here for price of added complexity in the framework code, but I’m not sure it’s worth it).

            The resolution of “global” dependencies (e.g. GameControls or CameraRig) are delegated to the parent container.

            To support that, I’ve just extended both Get methods in the Container class with following

            if (_parent != null)
            return ((IInternalContainer)_parent).Get(contract);

            Of course, the IInternalContainer have to be extended with Get signatures and _parent member should be added to Container class. But that’s it – minimal changes really.

            Probably such approach is not always the best way to go and often a simple hard-coded Factory would do just fine.
            But IMHO if you brought DI into your project, you can just use it everywhere as well 😉

            Anyways, I hope I was able to explain my use case more or less.

            Root Container:

            void SetupContainer()
            {
            container = new Container();

            // input
            container.BindSelf();
            container.BindSelf();
            container.BindSelf();

            // vehicle builder
            container.BindSelf();
            }

            Vehicle builder:

            public class VehicleBuilder
            {
            [Inject] IContainer ParentContainer { get; set; }

            public Vehicle Create()
            {
            // the “scope” container used to construct just a vehicle
            var container = CreateScopeContainer(ParentContainer);

            return container.Build();
            }

            IContainer CreateScopeContainer(IContainer parentContainer)
            {
            var container = new Container(parentContainer);

            container.BindSelf();
            container.BindSelf();
            container.BindSelf();
            container.BindSelf();

            return container;
            }
            }

            Usage:

            var vehicleBuilder = rootContainer.Build();
            var vehicle = vehicleBuilder.Create();

    1. Thank you for the input, it will be useful! I’ll be back on this one probably next week after the 18th, since I am off for holidays ;).

      Btw, in your specific case I am a bit concerned about performance. However something must be done.

  3. Hi. That was very interesting to find your site with the IoC framework for Unity, as I have just developed my own framework, and it looks very similar feature and usage cases wise. It’s funny how our solutions are matching, especially with factory things, and also the approach to the mono behaviours 🙂

    I wanted to comment on injection of MonoBehaviour as I think it can be still needed sometimes. In my project i have a generic view, that is MonoBehaviour and is created through the factory dynamically.
    So for different object types, the different instances of the generic view class are used.
    However, all these views should link to the same inspector properties – e.g. button skin, buttons, and so on. And the Inspector MonoBehaviour is a simple class, containing no logic, but just having some public references, that the
    GUI designer can customise.
    Therefore, this Inspector MonoBehaviour is statically present in the scene. And in this case I inject it automatically to any dynamically created instance of the view. Here is the example of the view:

    public class MergePanelUI : ContextMonoBehaviour, IMergePanelUI
    ….

    [Injection]
    public MergePanelInspector inspector { get; set;}

    So, whatever version of MergePanelUI I create, the MergePanelInspector will be injected automatically in this case. I don’t see at the moment any issues about doing it like this.

    1. WordPress swallows less and greater charaxters so the class signature was this with [] instead of “less-greater” brackets (I don’t know how to screen them)
      public class MergePanelUI [T, TChild] : ContextMonoBehaviour, IMergePanelUI[T, TChild]

    2. Hello Andrey,

      thanks for the comment. In Robocraft, which is a huge project, we never felt the need to inject monobehaviour. Injecting monobehaviour would go against my rules too, since a monobehaviour should be used ONLY as mean to extend a gameobject, therefore belonging only to the relative gameobject and nothing else. Destroying a monobehaviour, which is referenced somewhere else, is kind of awkward and one of the many Unity framework pitfalls.
      GUI are always managed by presenter (in a MVP scenario), so it’s the presenter that should be injected, never the view.

      1. Hi. I have several situations where I do inject MonoBehaviours. E.g. have a single coroutine manager for the whole application to start all coroutines on, that i can inject to any objects that start coroutine. Or for example I have a presentation that is aware of its view and injects the view. When instantiating the presentation, I want the view to be resolved automatically together as dependency, and instantiated. There can be many other cases, and while I’m also thinking, that the usage of MonoBehaviour should be careful, I think that I don’t want to limit of how people might want to use my framework.

        Therefore I have found a way to manage this. I call MonoBehaviours “remote objects” in relation to my context, as they are managed of remote framework (Unity). If my context does an injection of such an object, the instantiation is tracked to special RemoteObjectsRecord, that exists on context. Then, I have added destruction method to every factory – it means if factory has created an object, this factory can also destroy this object.

        A creation of object can cause the whole bunch of objects instantiated as dependencies. This includes usual objects and the mono behaviours. When factory destroys it’s instance it just reads all the mono behaviours that has been instantiated on its context, and destroys all of them. Like this I have it safe in all the situations, assuming of course that user only creates the objects through factories, and destroys them likewise.

        Another thing, that I made with this solution – is that the context now controls the switching between scenes. I’m doing each scene loading on a factory, thus tracking and auto-injecting dependencies on all the objects that appeared, and each scene has a separate context layer. That allowed me amongst other to easily load additive scenes, and just substractively unload them when needed.

        I know, it can be hard understandable without examples. I’m gonna make a good documentation and some articles when my framework is finished.

  4. Hello!

    Thanks for your IoC container. I am started using it in our Unity3D project.
    I have one question.
    You probably use the IoC container in a multiplayer game.
    In a multiplayer game GameObject should have NetworkBehaviour instead of MonoBehaviour component.
    The question is how to create a network GameObjects? When the network objects are created on the server scene, they will automatically appear on client scenes. How to control it? The same should be done for the Player GameObject.
    You can of course override the handlers to control spawn players and objects, but
    maybe you already have a ready-made solution?

    1. Hello,

      I am not sure if this question is really relevant to the article, however we don’t use NetworkObjects. All our network events are handled by us explicitly. Anyway you may want to try Photon Bolt, you will find it on the Asset Store.

  5. Thank you for this update. Great work, as always. Regarding the containers hierarchies i tried doing that (in cpp) and Was never Happy with the result, actually i preferred ti keep multiplex Independent containers from which some instances could be “shared” but that is fine manually. The fact is that Sharon bindings in a hierarchies way makes much less obvious which implementation is choosen

Leave a Reply