Search Unity

[Released] Adic - Dependency Injection Container

Discussion in 'Assets and Asset Store' started by Intentor, Mar 18, 2015.

  1. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.16.1 version is available on the Asset Store.

    Changelog

    Context Root extension
    • Fixed editor ExecutionOrderUtils script location, which was preventing build generation.
     
  2. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.16.2 version is available on the Asset Store.

    Changelog

    Context Root extension
    • Fixed children inspector fields display of ContextRoot.
     
  3. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.16.3 version is available on the Asset Store.

    Changelog

    Context Root extension
    • Fixed adding container by type on iOS devices.
     
  4. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.17 version is available on the Asset Store.

    Changelog

    Framework
    • Added support for Windows 10 Phone/Universal Apps (IL2CPP) and WebGL.
     
  5. Sam-K

    Sam-K

    Joined:
    Mar 23, 2013
    Posts:
    27
    Hi @Intentor ,
    The core reasons for using this framework is decoupled code hence clean code/architecture and testing. But what I would like to ask is
    • Why not use an ECS framework like Entitas, EgoCS, UFrame ECS? I mean what's the difference not it terms what DI and ECS is because in the end they both tend to provide decoupled code clean architecture, tests?
    • What are the differences and what are specific use cases when to use one over another?
    I'm want such framework because I'm very upset with coupling/dependency. Even If I make delegate events still have to register them somewhere else. And I want to test. I have studied StrangeIOC, Entitas.
    I have to do a lot of extra work with StrangeIOC. Entitas seems very good but, since I'm new to DOD I'm currently getting my head over how to make things specially not related to core game with it.
     
  6. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello Sam K!

    The main difference between Adic and the referred components (or any ECS in general) is that Adic doesn't enforce you on using any specific pattern/architecture, being just a DI container. Although it contains a Command extension (which is an architectural pattern), it's completely optional. The main focus of the project is to provide a simple, fast and nonintrusive DI container that allows the dev to keep the Unity way of doing things.

    However, the goal of any DI container or ECS component, as you said, is to contribute for decoupling. The choice between one and another, in my opinion, is related to two things, which I think may answer your second question:

    1. The kind of projects you're working on and your team (e g. uFrame is fantastic when you focus on tools for game designers and artists).

    2. The need of the developer for a set of tools that can support the kind of development he's looking for (e.g. the dev doesn't want to handle systems manually - by for example wiring injections -, but prefer a tool that can help him achieve that level of control more automagically).

    Adic can turn itself into, let's say, a full ECS, by decoupling systems from components through injection (which is the way I develop, creating systems - read: regular classes with a given purpose - that act on game objects), without the need (most of times!) for a more complex code infrastruture. This also allows you to create tests for all of your systems, in a way that game objects - and its behaviours - usually just interpret data that comes from the systems itself.

    I'm working on a simple game demo for Adic, which will showcase some of those concepts.

    In the end, I think, it's important to test the available options and analyse which one better suits your projects and dev style. :)

    Hope that helps! Feel free to ask if you have any other questions!
     
    Last edited: Mar 5, 2016
  7. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.18 version is available on the Asset Store.

    Changelog

    Bindings Printer Extension
    • Updated rendering to use GUILayout methods.
    • Fix ScrollView when displaying lots of bindings.
    Context Root extension
    • Added base type for children injection on scenes.
    • Added inactive game objects selection when injecting on children of the root.
    • Fixed making scene dirty when changing values on inspector during Play Mode.
     
  8. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    @Intentor I find it interesting how Sebastiano Mandalà's research on IoC/DI is attributed as a basis for both Zenject and Adic implementations of IoC/DI. But both Zenject and Adic stop short of Sebastiano Mandalà's final postings where he recommends not using IoC Containers. I'd like to hear your take on this.
     
  9. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hey, @TokyoDan, that's a fantastic question! :D

    Sebastiano's work was a great inspiration for Adic (and Zenject). Although his final article about The truth behind Inversion of Control seems to go against IoC and more towards an entity component system approach, I think the real question is all about good coding practices rather than discussing the dangerousness of IoC (or any other pattern).

    Take for instance the example from the article. It's much more about coding with care than using IoC itself. In my opinion, IoC, ECS or any other tool or architecture have no use if the developer don't have a proper mindset to code with clarity and quality in mind. Focusing on a SOLID approach will always give better results than just following blindly any standards a tool - or pattern - may offer (that's why Adic tries to not enforce any particular architecture, letting the developer choose what works best for his/her project).

    I appreciate IoC containers (especially Adic ;)) because they help me keeping the code (as much as possible) decoupled, focusing on abstractions that lead to high specialized small classes, which in the end allows for a more flexible, reusable and portable codebase. However, if no IoC container was used, I'd keep the same mindset when developing.

    I usually encourage devs beginning with IoC to try some tools before commiting to a single one and even risk a Poor's Man DI approach so they can learn the mysteries about the pattern - and check whether it's really what the project needs and the team feels confortable with (in fact I can say that about any tool, pattern, architecture or miracle coding practice!).

    In the end, I believe it's not about the tools (or patterns), but the way you code. Any architecture with proper tools can produce beautiful and functional code - or not! :)
     
    Last edited: Jun 2, 2016
  10. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    Great explanation. Thanks.

    Besides trying a Poor's Man DI approach, is there anything else that I could read that is not directly about IoC/DI or ECS that'd get me in the right frame of mind to correctly use Adic? Would learning more about SOLID help?
     
    Last edited: Jun 2, 2016
  11. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello again, @TokyoDan!

    Besides reading some articles aboud SOLID (like the one on my previous answer), I'd recommend three amazing programming books that I love, respect and usually use for reference on good coding practices:
    1. Code Complete, by Steve McConnell
    2. Clean Code, by Robert C. Martin
    3. The Pragmatic Programmer, by Andrew Hunt and David Thomas
    These books are classics that every programmer should read once in a while to refresh our coding skills.

    Additionally, take a look at the Recommended Reading for Developers by Jeff Atwood from the Coding Horror Blog. There are some other interesting books on his list.

    Hope that helps you code even better! :)
     
  12. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.19 version is available on the Asset Store.

    Changelog

    Framework
    • Add instance resolution mode, so it's possible to return null on types that are not bound to the container. [Resolves #46]
    • Fix singleton instances being created for each binding type. [Issue #44]
    Event Caller extension
    • Fix adding of already added binding. [Issue #44]
    • Update Time.deltaTime conditional to use Mathf.Approximately instead of direct comparison. [Issue #44]
     
  13. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.19.1 version is available on the Asset Store.

    Changelog

    Event Caller extension
    • Fix incorrect checking of already added bindings. [Issue #51]
     
    Last edited: Jul 28, 2016
  14. CADS

    CADS

    Joined:
    Jun 20, 2013
    Posts:
    13
    Hi,
    I just discovered this nice DI container and must say that I really like it so far :) I am thinking about moving some tasks of mine over to the command extension. I was wondering how I would be able to cancel a running command... is this even a good practice?
    My use case would be:
    The application dispatches a "load some stuff from the web command". Now because the users switches to a different area of the application I need to cancel the command, because it is no longer needed.
    I am very interested to hear your opinion on that, thank you in advance :)

    Anyway, great library! Thank you for sharing! :)
     
  15. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello, @CADS!

    Thanks for using Adic! Glad to know you're having a good time using it! :D

    About your question, it's a good practice to move tasks to commands and eventually cancel them. Think of commands either as responses to events and as tasks that can be executed a single time, continuously or from time to time - so it makes sense to stop them when needed.

    Regarding canceling, It's possible to stop commands by type by calling the ReleaseAll<CommandType>() method of the CommandDispatcher (the docs contain examples on how to use it). It's also possible to cancel/release a given command, if you have its instance, by calling the Release(instance) method. To use the methods, just inject an instance of the dispatcher (CommandDispatcher) into the class that will release them.

    Hope that helps!

    If you have any further questions or ideas on improving Adic, just let me know!
     
    Last edited: Jul 28, 2016
  16. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.20 version is available on the Asset Store.

    Changelog

    Framework
    • Add inject on methods. [Issue #48]
    • Fix unbinding of singleton types. [Issue #50]
    • Construct attribute is now deprecated. Please use Inject instead.
    • PostConstruct attribute is now deprecated. Please use Inject instead.
    Bindings Printer Extension
    • Fix inconsistent space around container name.
     
  17. CADS

    CADS

    Joined:
    Jun 20, 2013
    Posts:
    13
    Thank your for the fast response @Intentor
    I did already see the Release methods of the dispatcher. Unfortunately I do not see a way to get a hold of the specific ICommand instance. I think I will go with the ReleaseAll<> by type.
     
  18. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello again, @CADS!

    Thinking about how to get command instances, the docs are not intuitive on that regard. I created an issue to improve the way commands are released (and also the docs!), maybe allowing an ID to be added to commands for later use during releasing.

    Thanks for the feedback!
     
  19. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.20.1 version is available on the Asset Store.

    Changelog

    Commander extension
    • Fix command pooling. [Issue #53]
     
  20. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.20.2 version is available on the Asset Store.

    Changelog

    Framework
    • Fix singleton binding when ResolutionMode is RETURN_NULL. [Issue #54]
     
  21. maxxa05

    maxxa05

    Joined:
    Nov 17, 2012
    Posts:
    186
    Potential user here. I'm currently using Zenject, but as I'm porting to Xbox One, well, it seems to be problematic. It makes extensive use of Linq and reflection.

    You say that you cache stuff to minimize the use of reflection, is it possible to not use reflection at all at runtime? Can it be completely compiled so it works well with both mono in AOT-only or IL2CPP? Also, do you use Linq at runtime (seems not when looking at your code)?

    If your system works with my needs, I'll change Zenject for your system, and I'll give you a nice donation for all the time you'll save me :)
     
  22. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello @maxxa05!

    Thanks for considering Adic for your projects! :D

    Although I've not tested Adic on Xbox One, it works AOT-only compiled by IL2CPP (on iOS) and doesn't use Linq. However it does use reflection to obtain type details and to create cached calls (unfortunately there's no other way to do that...), mostly during container creation.

    But even with the reflection performance caveats, Adic is developed to run as fast as possible, so I don't think you should face any performance issues on consoles because of it (I've never faced any performance problems on mobile caused by Adic, so on a console I expect it to be the same).

    I'd recommend that you compile the demo scenes and see if they work as expected on Xbox One.

    If you need any further help or clarifications, just let me know! And if you do choose Adic for your project on Xbox One, please keep in touch so I can update the docs about compatibility and to help you on any problems that may occur.

    Happy coding!
     
    Last edited: Sep 16, 2016
  23. maxxa05

    maxxa05

    Joined:
    Nov 17, 2012
    Posts:
    186
    I tried to build your scene Factory with mono and I got the following exception:

    ExecutionEngineException: Attempting to JIT compile method '(wrapper dynamic-method) Adic.InjectionContainer:InjectionContainer ()' while running with --aot-only.

    I may try with IL2CPP, but right now we are not sure it's stable enough for us to port on it. Thanks anyway!
     
  24. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello, @maxxa05!

    Sorry to hear that! Although, given the message, I think what the problem may be.

    If you wanna git Adic another try on the XOne, I uploaded here a fix that I think may solve the problem.

    Anyway, thanks for your interest in Adic! :D
     

    Attached Files:

  25. FodderMK

    FodderMK

    Joined:
    Jan 21, 2013
    Posts:
    5
    I'd like to get some additional functionality added to the EventCallerContainerExtension, specifically something along the lines of IQuitable (OnApplicationQuit) and IPausable (OnApplicationPause). I could make a pull request with this additional functionality but before I did so I wanted to get your thoughts on best practices. Should functionality like this be added directly to EventCallerContainerExtension or did you have a different idea in mind with extending its functionality?
     
  26. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello, @FodderMK!

    I can't see why not add those events to the Event Caller extension!

    The idea of the extension is to provide access to Unity events, so it's a perfect fit for that.

    Just add a different interface for each event, to create a better seggregation.

    When you send the PR, I'll update the docs.

    Thanks for using Adic and helping make it even better!
     
  27. cbrack

    cbrack

    Joined:
    Apr 26, 2016
    Posts:
    10
    Hi, Is there any complete example of [Inject] in a standard class (no command, mono, .... )?
     
  28. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello, @cbrack!

    The first example that comes with Adic (Hello World) may be the example you're looking for.

    Although it doesn't inject anything in the regular class (HelloWorld.cs), if it had any field/property with Inject attribute they would be injected.

    Any class that is bound to the container (i.e. container.Bind<Type>()) can receive injections regularly.

    Using the HelloWorld class as example, if you had it and a MyService class bound to the container, you could do something like:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. namespace Adic.Examples.HelloWorld {
    5.     /// <summary>
    6.     /// Simple class that displays "Hello, world!".
    7.     /// </summary>
    8.     public class HelloWorld  {
    9.         /// <summary>My injected service.</summary>
    10.         [Inject]
    11.         public MyService myService { get; set; }
    12.  
    13.         /// <summary>
    14.         /// Displays "Hello, world!" on console.
    15.         /// </summary>
    16.         public void DisplayHelloWorld() {
    17.             Debug.Log("Hello, world!");
    18.         }
    19.     }
    20. }
    You could even use Unity Update method on regular classes if they are bound to the container (soon other events will also be supported)!

    Hope that helps!

    If you have any further questions, just let me know!
     
  29. cbrack

    cbrack

    Joined:
    Apr 26, 2016
    Posts:
    10
    Thank you for your reply! My problem is currently, that I don't know how to create instances of classes with [Inject] somewhere in the code. As example I have the following:

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using Adic;
    6. using UnityEngine;
    7.  
    8. namespace MyTest
    9. {
    10.   public class OneRoot : ContextRoot
    11.   {
    12.     public override void SetupContainers()
    13.     {
    14.       //Create the container.
    15.       var container = this.AddContainer<InjectionContainer>();
    16.  
    17.       //Bind a class to itself.
    18.       container.Bind<TheClass>().ToSelf()
    19.                .Bind<ITheSingleton>().ToSingleton<TheSingleton>();
    20.     }
    21.  
    22.     public override void Init()
    23.     {
    24.       InjectionTest itest = new InjectionTest();
    25.       itest.DisplayValue();    
    26.     }
    27.   }
    28. }
    and

    Code (CSharp):
    1. using Adic;
    2. using System;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. using System.Text;
    6. using UnityEngine;
    7.  
    8. namespace MyTest
    9. {
    10.   public interface ITheSingleton
    11.   {  
    12.     int GetValue();  
    13.   }
    14.  
    15.   public class TheSingleton : ITheSingleton
    16.   {  
    17.     public int GetValue()
    18.     {
    19.       return 20;
    20.     }  
    21.   }
    22.  
    23.   public class TheClass
    24.   {
    25.     [Inject]
    26.     public ITheSingleton _mySingleton;
    27.  
    28.     public int TheValue()
    29.     {
    30.       return _mySingleton.GetValue();
    31.     }
    32.   }
    33.  
    34.   public class InjectionTest
    35.   {
    36.     private TheClass _myClass = new TheClass();
    37.  
    38.     public void DisplayValue()
    39.     {
    40.       int v = _myClass.TheValue();
    41.       Debug.Log(v);
    42.     }  
    43.   }
    44. }
    45.  
    The _mySingleton remains null. How could I handle that?
     
  30. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello again, @cbrack!

    To perform injection on regular classes, the easier way is to resolve them from the container. Take a look at Manual type resolution on the docs or the Hello World example, which also uses manual type resolution.

    Basically you have to call the Resolve() method on the container. For your InjectionTest class example, giving you already have a reference to the container instance, instead of instantiating the TheClass class, you should get it from the container. It'd be something like, e.g. in the InjectionTest class constructor:

    Code (CSharp):
    1. public InjectionTest(IInjectionContainer container) {
    2.     _myClass = container.Resolve<TheClass>();
    3. }
    You could also inject MyClass into InjectionTest and resolve only the later:

    Code (CSharp):
    1. public class InjectionTest  {
    2.     [Inject]
    3.     public TheClass myClass;
    4.  
    5.     public void DisplayValue()  {
    6.         int v = _myClass.TheValue();
    7.         Debug.Log(v);
    8.     }
    9. }
    10.  
    11. ...
    12.  
    13. InjectionTest injectionTest = container.Resolve<InjectionTest>();
    14. injectionTest.DisplayValue();
    Theoretically any class can be resolved through Adic if instance resolution mode is ALWAYS_RESOLVE.

    Hope that helps!

    If you you have any more questions, just reply to the thread!
     
    Last edited: Jan 19, 2017
    cbrack likes this.
  31. cbrack

    cbrack

    Joined:
    Apr 26, 2016
    Posts:
    10
    Thank you very much for your quick and very helpful reply!
    One little question: Is it possible to access the container with the Adic framework anywhere in the code?
     
    Intentor likes this.
  32. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello again, @cbrack!

    You could inject the container on any classes that are bound to it:

    Code (CSharp):
    1. public class SomeBoundClass  {
    2.     [Inject]
    3.     public IInjectionContainer container;
    4.     ...
    5. }
    For classes that are not bound to the container, you could add it manually during context root initialization (e.g. instantiating the class manually or by resolving it). I'd only not recommend to create a singleton variable to hold the container - this could lead to unwanted instances on scene changes. For scene behaviours, you could use scene injection.

    Hope that further clarifies the issue!

    If you have any other inquiries, just reply to the thread!
     
    cbrack likes this.
  33. cbrack

    cbrack

    Joined:
    Apr 26, 2016
    Posts:
    10
    Great! Thanks a lot!
     
  34. cbrack

    cbrack

    Joined:
    Apr 26, 2016
    Posts:
    10
    Hello again! So far all went well with the great hints! Now i'm trying to use shared containers for different scenes to keep a singleton class alive without losing data of the singleton. Is there any example showing how to do this? Thanx!
     
  35. cbrack

    cbrack

    Joined:
    Apr 26, 2016
    Posts:
    10
    I tried a little bit more and came up to ContextRoot in the first scene with two containers, one (not destroyed) for surviving singletons and one (destroyed) for all bindings used in the first scene. For the second scene I have a ContextRoot with one container for all bindings used in the second scene. The two containers in the first scene are disjointed, thus the singletons only are bounded in the first container. But now there are several problems, even in the first scene:
    1) A class bounded in the second container is searching the injected singleton also in the second container where it is not bounded -> exception
    2) A Monobehaviour with [InjectFromContainer("Container1")] is working, but I could not inject members also from the second container.
    3) My first thought, using an identifier for the singleton .As("MySingleton") and inject it with [Inject("MySingleton")] does not work here. The singleton is also searched in the second container -> exception
    4) Binding the singleton also to the second container leads to two instances of the singleton.
    5) Resolving a non monobehaviour class with container.Resolve is bounded to a container and ignores [InjectFromContainer] -> exception

    My intention is still to have one remaining singleton for all scenes and additionally non-permanent injection objects which could also depend on the singleton.
    It might be that I'm doing something wrong, but I don't know what. Is there any way to solve these issues?

    Thanks a lot!
     
  36. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.23 version is available on the Asset Store.

    Changelog

    Framework
    • Added Android IL2CPP support. [Issue #71]
    • Removed registering/unregistering of extensions by instance. [Issue #70]
    Event Caller extension
    • Added support for Unity events OnApplicationFocus, OnApplicationPause, OnApplicationQuit, LateUpdate and FixedUpdate. [Pull Request #68 and Issue #69]
    • Fixed scene loading causing events to be lost. [Issue #70]
     
    Last edited: Feb 2, 2017
  37. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello, @cbrack!

    Sorry for the late response. I didn't receive any alerts about your messages.

    Regarding your questions, I do recommend taking a look at Multiple Scenes in the docs. The test https://github.com/intentor/adic/tree/master/src/Assets/Tests/MultiScene may also help you better understand how to achieve true singletons between scenes.

    About behaviour injection, you can read the docs about it to clarify the way it's handled. But if you have more than one container in the same scene and both provide instances for behaviours, the best way is to use a Base Type scene injection. In this case, I recommend that every container handles a given set of types and both are configured to RETURN_NULL resolution mode.

    However, if you request a type from both containers, currently it'll exist on all containers in which it's resolved. There's an old request that I'll try to look into ASAP about subcontainers. If this feature was available, both containers could work together as one, without any further configurations (and I also need this feature for my projects! :p), in a way that if a type is not found in the first one, it would automatically analyze all available containers.

    Hope that helps! If you need any further explanations, just let me know!
     
    Last edited: Feb 2, 2017
    cbrack likes this.
  38. Raidenwins

    Raidenwins

    Joined:
    Dec 18, 2012
    Posts:
    132
    I have a question about method injection for MonoBehaviors. It says in the Adic documentation that a good practice is to use method injection as constructors in MonoBehaviors. So let's say I have the method initialize() in a MonoBehavior class that I want to use as a constructor, i.e. something like this:

    Code (CSharp):
    1. public class MyClass : MonoBehavior
    2. {
    3.     private IMyInterface _myImplementation;
    4.  
    5.     void Start()
    6.     {
    7.         // Should 'initialize' be called from here??
    8.         initialize();
    9.     }
    10.  
    11.     [Inject]
    12.    void initialize(IMyInterface param)
    13.     {
    14.         _myImplementation = param;
    15.     }
    16. }
    Would initialize() be called from the Start() method or would Adic call it automatically and pass to it the necessary implementation of type IMyInterface?

    Also, for MonoBehaviors, is it better to use method injection (as above) or public field injection, like in the example below (from the documentation):

    Code (CSharp):
    1. using Unity.Engine;
    2.  
    3. namespace MyNamespace {
    4.     /// <summary>
    5.     /// My MonoBehaviour summary.
    6.     /// </summary>
    7.     public class MyBehaviour : MonoBehaviour {
    8.         /// <summary>Field to be injected.</summary>
    9.         [Inject]
    10.         public SomeClass fieldToInject;
    11.  
    12.         protected void Start() {
    13.             this.Inject();
    14.         }
    15.     }
    16. }
     
  39. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello @Raidenwins!

    Method injection is a nice way to add constructors on MonoBehaviours, but properties can also be used just fine. I'd say that it's more a matter of architectural patterns used by the project. However, particularly, I do prefer method injection because it makes us better organize dependencies - usually, with properties, there's a tendency to add more injections than necessary and make the behavior more complex than it should be.

    About your initialize question, there's no need to call it from the Start() method (it wouldn't work properly because it's not possible to inject from there). You should correctly setup a Behavior injection strategy and Adic will call it automatically.

    Hope that helps! :D

    If you have any further questions, just reply to the topic!
     
  40. Raidenwins

    Raidenwins

    Joined:
    Dec 18, 2012
    Posts:
    132
    Thanks for the answer @Intentor. I prefer method injection also. I'll give it a shot and ask here if I have any more questions.

    UPDATE: I have a question already. I am confused about method injection still. The link that you gave above takes me to the MonoBehavior Injection part, which looks to me like field and property injection. It also states there, "To perform injection on custom MonoBehaviour fields and properties, simply call the Inject() extension method of the MonoBehaviour".

    As for method injection, can the method be private or does it have to be public?
     
    Last edited: Feb 15, 2017
  41. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello, @Raidenwins!

    You can use either member or method injection on behaviours.

    The MonoInjection extension allows a simple way to inject on behaviours from any available available container. This is required because most behaviours are not bound to the container (i.e. binding from ToGameObject()/ToPrefab() methods). However, you can use scene injection to perform injection on behaviours without the MonoInjection extension (please refer to the docs).

    Currently, injection on methods/fields/properties can only occur on public members. If you think it'd be a nice feature to have, please open an issue on the GitHub project page!

    Hope that helps!

    If you have any other questions, just reply to the thread!
     
    Last edited: Mar 1, 2017
  42. Raidenwins

    Raidenwins

    Joined:
    Dec 18, 2012
    Posts:
    132
    Thanks. I think I got a little bit further, but I am getting an error now. The error is the following:

    It's happening when I call this.Inject() in the Start method of my MonoBehavior. Here is the relevant code. First, my context root:

    Code (CSharp):
    1. public class MyRoot : Adic.ContextRoot
    2. {
    3.     public override void Init()
    4.     {
    5.     }
    6.  
    7.     public override void SetupContainers()
    8.     {
    9.         this.AddContainer<InjectionContainer> ()
    10.             // Register any extensions the container may use.
    11.             .RegisterExtension<UnityBindingContainerExtension> ()
    12.  
    13.             .Bind<IMyTestInterface> ().To (new MyTestClass ())
    14.  
    15.             .Bind<MyMonoBehavior> ().ToGameObject ();
    16.     }
    17.  
    18. }
    And then, my MonoBehavior class:

    Code (CSharp):
    1. public class MyMonoBehavior : MonoBehaviour
    2. {
    3.     private IMyTestInterface _myTestClass;
    4.  
    5.     [Inject]
    6.     public void Initialize(IMyTestInterface myTestClass)
    7.     {
    8.         _myTestClass = myTestClass;
    9.     }
    10.  
    11.     void Start ()
    12.     {
    13.         this.Inject (); // ERROR HAPPENS HERE
    14.     }
    15.  
    16.     void Update()
    17.     {
    18.     }
    19. }
    It doesn't look like my Initialize() method is being called at all. I put a breakpoint in it and it's not getting hit.

    And here is what is happening in class InjectionUtil (the value of argument identifier is also null):



    What am I doing wrong here?
     
    Last edited: Feb 17, 2017
  43. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello, @Raidenwins!

    Maybe your problem has to do with something that I've just noticed in the docs: the lack, on the Quick start, of a sentence explaining that you should attach the context root to an empty game object!

    On the attached adic-test.zip file I've created a simple example using your code with the context root attached to an empty game object and everything just worked fine.

    Hope that helps!
     

    Attached Files:

    Raidenwins likes this.
  44. Raidenwins

    Raidenwins

    Joined:
    Dec 18, 2012
    Posts:
    132
    Ah, yes! That was the missing link. I was wondering how the context root tied in with the rest of the code and now I know.

    A couple of questions: why do you make your Start methods in MonoBehaviours protected (even when you are not overriding them), as opposed to private (like they are by default)? And two, is the Inject() method call in the Start() method of a MonoBehaviour necessary when using method injection? I put a breakpoint in my injected method and I noticed that if I don't have a call to Inject(), the method still gets called, even though it seems like it's not initializing the dependency correctly (its value is null).
     
    Last edited: Feb 20, 2017
  45. Raidenwins

    Raidenwins

    Joined:
    Dec 18, 2012
    Posts:
    132
    @Intentor can I please get an answer to my questions above. I also have another question (about constructor injection), but I'll wait to have my previous two questions answered first.
     
  46. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello, @Raidenwins!

    Sorry for the late response. I was out on vacation! :D

    The use of protected Start methods is just my way of organize those methods, so they could be correctly overriden
    if needed on derived classes. You could make them private if you think that's the right design choice for the class.

    If you're using method injection, something has to inject to that method, which depends on which behavior injection strategy you're using. You could:
    1. use a base behavior with the Inject() method;
    2. call Inject() manually on each Start() method of each behavior;
    3. use a scene wide injection strategy (which by the way is the best approach in my opinion).
    If your dependency is null, maybe it was not added to the container correctly. I recommend you use the Bindings Printer window to check if the dependency was correctly added.

    Hope that helps! :D

    If you have any questions about any of those injection strategies or any other Adic related topic, just reply to the thread!
     
    Raidenwins likes this.
  47. Raidenwins

    Raidenwins

    Joined:
    Dec 18, 2012
    Posts:
    132
    Thanks for the reply @Intentor. Hope you had a good vacation.

    What you said makes sense. As for my question about constructor injection, I have the following scenario:

    In my context root:
    Code (CSharp):
    1. public class MyContext : Adic.ContextRoot
    2. {
    3.  
    4.     public override void Init()
    5.     {
    6.     }
    7.  
    8.     public override void SetupContainers()
    9.     {
    10.         this.AddContainer<InjectionContainer> ()
    11.             // Register any extensions the container may use.
    12.             .RegisterExtension<UnityBindingContainerExtension> ()
    13.  
    14.             .Bind<MyFirstMonoBehavior>().ToGameObject("EmptyGameObject")
    15.  
    16.             .Bind<IMyInterface>().To(new MyClass()) // ERROR HAPPENS HERE
    17.  
    18.             .Bind<MySecondMonoBehavior> ().ToGameObject ();
    19.     }
    20.  
    21. }
    Next, the relevant code for "MySecondMonoBehavior". It inherits from BaseMonoBehavior, which calls Inject() in its Start method:
    Code (CSharp):
    1. public class MySecondMonoBehavior : BaseMonoBehavior
    2. {
    3.     private MyFirstMonoBehavior _firstMonoBehavior;
    4.     private IMyInterface _myClassInstance;
    5.  
    6.     [Inject]
    7.     public void Initialize(IMyInterface myClassInstance, MyFirstMonoBehavior firstMonoBehavior)
    8.     {
    9.         _firstMonoBehavior = firstMonoBehavior;
    10.         _myClassInstance = myClassInstance;
    11.     }
    12.  
    13.     protected override void Start ()
    14.     {
    15.         base.Start();
    16.     }
    17.  
    18. }
    And here is the declaration of interface "IMyInterface":
    Code (CSharp):
    1. public interface IMyInterface
    2. {
    3.     void DoSomething();
    4. }
    And finally, the implementation of "MyClass":
    Code (CSharp):
    1. public class MyClass : IMyInterface
    2. {
    3.     private MyFirstMonoBehavior _firstMonoBehavior;
    4.  
    5.     public MyClass(MyFirstMonoBehavior firstMonoBehavior)
    6.     {
    7.         _firstMonoBehavior = firstMonoBehavior;
    8.     }
    9.  
    10.     public void DoSomething()
    11.     {
    12.     }
    13. }
    The problem is that I get the following error in "MyContext" on the line that binds IMyInterface to an instance of MyClass (line 16):

    So it looks like I need to pass an argument to the constructor of MyClass in the binding, but I don't know how to do that. How can I resolve this issue?
     
  48. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello @Raidenwins!

    Looks like you're trying to instantiate MyClass without passing the required behavior in the constructor (line 16 of MyContext).

    If you want Adic to solve the dependency (which is the way I recommend), you should first bind MyFirstMonoBehavior to the container. If you want to solve it manually (which in my opinion doesn't make sense, given you're using a dependency injection container), you have to pass an instance of MySecondMonoBehavior during the new MyClass() statement: new MyClass(mySecondMonoBehaviorInstance).

    Considering the recommended approach:

    Code (CSharp):
    1. this.AddContainer<InjectionContainer>()
    2.     .RegisterExtension<UnityBindingContainerExtension>()
    3.     .Bind<MyFirstMonoBehavior>().ToGameObject("EmptyGameObject")
    4.     .Bind<MySecondMonoBehavior>().ToGameObject()
    5.     .Bind<IMyInterface>().To<MyClass>();
    Hope that helps! :D

    If you have any further inquires about Adic, just reply to the thread!
     
    Last edited: Mar 3, 2017
  49. Raidenwins

    Raidenwins

    Joined:
    Dec 18, 2012
    Posts:
    132
    Hi @Intentor, when I use what you suggested above I no longer get a compilation error, but when I run the game I get this error:

    Also, the "Initialize" method in MySecondMonoBehavior is not called anymore.

    Why would that be happening?

    And on that note, any chance you could add an example (to the code base) of constructor injection for a non-Mono Behaviour class?
     
    Last edited: Mar 3, 2017
  50. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello, @Raidenwins

    The order of bindings matter. In my last example I was just explaining about the binding of MyClass, without taking into account binding orders.

    The correct way to perform the bindings would be:

    Code (CSharp):
    1. this.AddContainer<InjectionContainer>()
    2.    .RegisterExtension<UnityBindingContainerExtension>()
    3.    .Bind<IMyInterface>().To<MyClass>()
    4.    .Bind<MyFirstMonoBehavior>().ToGameObject("EmptyGameObject")
    5.    .Bind<MySecondMonoBehavior>().ToGameObject();
    You have to first bind any types that subsequent bindings would use (in the example, MyClass and MyFirstMonoBehavior are injected into MySecondMonoBehavior, so they must be bound first).

    Hope that helps! :D
     
    Last edited: Mar 3, 2017