From f48204f16f2ab979ade4397c27a8555f034054d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kurai=20Andr=C3=A1s?= Date: Fri, 14 Oct 2022 13:23:59 +0200 Subject: [PATCH 1/5] add inheritable injecting base class --- .../Injecter.Unity/MonoBehaviourInjected.cs | 35 +++++++++++++++++++ .../MonoBehaviourInjected.cs.meta | 11 ++++++ 2 files changed, 46 insertions(+) create mode 100644 UnityProject/Injecter.Unity/Assets/Injecter.Unity/MonoBehaviourInjected.cs create mode 100644 UnityProject/Injecter.Unity/Assets/Injecter.Unity/MonoBehaviourInjected.cs.meta diff --git a/UnityProject/Injecter.Unity/Assets/Injecter.Unity/MonoBehaviourInjected.cs b/UnityProject/Injecter.Unity/Assets/Injecter.Unity/MonoBehaviourInjected.cs new file mode 100644 index 0000000..51eb0da --- /dev/null +++ b/UnityProject/Injecter.Unity/Assets/Injecter.Unity/MonoBehaviourInjected.cs @@ -0,0 +1,35 @@ +using Microsoft.Extensions.DependencyInjection; +using UnityEngine; + +namespace Injecter.Unity +{ + /// + /// Inheriting from this class will inject everything marked with the + /// + public abstract class MonoBehaviourInjected : MonoBehaviour + { + [Inject] private IScopeStore _store; + + protected IServiceScope Scope { get; private set; } + + /// + /// Call the base function before accessing injected members + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Roslynator", "RCS1213:Remove unused member declaration.", Justification = "Unity method")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "Unity method")] + protected virtual void Awake() => Scope = CompositionRoot.ServiceProvider.GetRequiredService().InjectIntoType(this, true); + + /// + /// Call the base function after accessing injected members + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Roslynator", "RCS1213:Remove unused member declaration.", Justification = "Unity method")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "Unity method")] + protected virtual void OnDestroy() + { + _store.DisposeScope(this); + + Scope = null; + _store = null; + } + } +} diff --git a/UnityProject/Injecter.Unity/Assets/Injecter.Unity/MonoBehaviourInjected.cs.meta b/UnityProject/Injecter.Unity/Assets/Injecter.Unity/MonoBehaviourInjected.cs.meta new file mode 100644 index 0000000..654d871 --- /dev/null +++ b/UnityProject/Injecter.Unity/Assets/Injecter.Unity/MonoBehaviourInjected.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1a9841611fcce0a48b63679ef9594661 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 7c7ff6cc8fee61676a5b554f922a38c829d10d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kurai=20Andr=C3=A1s?= Date: Fri, 14 Oct 2022 13:30:28 +0200 Subject: [PATCH 2/5] only patch types which are not inheriting from MonoBehaviourInjeted --- .../Injecter.Unity/Editor/MonoInjecterFinder.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/UnityProject/Injecter.Unity/Assets/Injecter.Unity/Editor/MonoInjecterFinder.cs b/UnityProject/Injecter.Unity/Assets/Injecter.Unity/Editor/MonoInjecterFinder.cs index 4199602..f174447 100644 --- a/UnityProject/Injecter.Unity/Assets/Injecter.Unity/Editor/MonoInjecterFinder.cs +++ b/UnityProject/Injecter.Unity/Assets/Injecter.Unity/Editor/MonoInjecterFinder.cs @@ -35,8 +35,16 @@ private static bool CanBeInjected(MonoBehaviour component) if (!_typeCache.TryGetValue(type, out var isInjectable)) { - var members = TypeHelpers.GetInjectableMembers(type); - isInjectable = members.Count != 0; + if (type.IsSubclassOf(typeof(MonoBehaviourInjected))) + { + isInjectable = false; + } + else + { + var members = TypeHelpers.GetInjectableMembers(type); + isInjectable = members.Count != 0; + } + _typeCache.Add(type, isInjectable); } From bdd1be68980dde00ce4d757fbd5af043b5795103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kurai=20Andr=C3=A1s?= Date: Fri, 14 Oct 2022 13:33:48 +0200 Subject: [PATCH 3/5] monoinjector does not inject into MonoBehaviourInjected derived classes --- .../Injecter.Unity/Assets/Injecter.Unity/MonoInjector.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UnityProject/Injecter.Unity/Assets/Injecter.Unity/MonoInjector.cs b/UnityProject/Injecter.Unity/Assets/Injecter.Unity/MonoInjector.cs index b9a56b9..7b1b70c 100644 --- a/UnityProject/Injecter.Unity/Assets/Injecter.Unity/MonoInjector.cs +++ b/UnityProject/Injecter.Unity/Assets/Injecter.Unity/MonoInjector.cs @@ -26,6 +26,8 @@ private void Awake() var component = components[i]; if (component == null || component == this) continue; + if (component.GetType().IsSubclassOf(typeof(MonoBehaviourInjected))) continue; + injecter.InjectIntoType(component, true); owners.Add(component); } From ee34f4e4e9617413dea8cb22216d281c56f694cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kurai=20Andr=C3=A1s?= Date: Fri, 14 Oct 2022 14:07:41 +0200 Subject: [PATCH 4/5] add documentation on new inheritable type --- Documentation/documentation/injecter.unity.md | 49 ++++++++++++++++--- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/Documentation/documentation/injecter.unity.md b/Documentation/documentation/injecter.unity.md index 37b9fa4..7105c8d 100644 --- a/Documentation/documentation/injecter.unity.md +++ b/Documentation/documentation/injecter.unity.md @@ -14,9 +14,13 @@ The `Injecter.Unity` lets you set up the following flow: - A "composition root" is initialized part of the entry point of the application - Create a script which needs to be injected -- Add `MonoInjector` to the `GameObject` hosting the script -- `MonoInjector` runs at `Awake`, and it's execution order (`int.MinValue` - first) is run before your own component's `Awake`. Every injected script will have it's own `IServiceScope` derived from the root scope. This scope can be retrieved through the `IScopeStore`, and the owner of the scope is the script being injected -- When the `GameObject` is destroyed, `MonoDisposer` will run during the `OnDestroy` method, with an execution order of `int.MaxValue` - last +- Choose an injection method: + - Either use the helper components + - Add `MonoInjector` to the `GameObject` hosting the script + - `MonoInjector` runs at `Awake`, and it's execution order (`int.MinValue` - first) is run before your own component's `Awake`. Every injected script will have it's own `IServiceScope` derived from the root scope. This scope can be retrieved through the `IScopeStore`, and the owner of the scope is the script being injected + - When the `GameObject` is destroyed, `MonoDisposer` will run during the `OnDestroy` method, with an execution order of `int.MaxValue` - last + - Or derive from `MonoBehaviourInjecter` + - The base class `Awake` method will do the injections, and the `OnDestroy` method will dispose of the scope ## Getting started @@ -80,7 +84,9 @@ public static class AppInstaller } ``` -### Inject into `MonoBehaviours` +### Inject using the helper components + +#### Inject into `MonoBehaviours` Create a script which will receive injection @@ -92,16 +98,40 @@ public class MyScript : MonoBehaviour } ``` -### Add `MonoInjector` +#### Add `MonoInjector` If you decorate your script with `[RequireComponent(typeof(MonoInjector))]` then, when adding the script to a `GameObject` the editor will add the `MonoInjector` and the `MonoDisposer` script to your `GameObject`. If for some reason this does not happen (for example, when changing an already living script into one needing injection), either add the `MonoInjector` component manually, or use the editor tools included to add the missing components to scenes or prefabs (will search all instances) through the editor menu `Tools / Injecter / ...` +### Inject by inheriting from `MonoBehaviourInjected` + +Create a script which will receive injection + +```csharp +public class MyScript : MonoBehaviourInjected +{ + [Inject] private readonly IMyService _service = default!; + + protected override void Awake() + { + base.Awake(); + + // Custom logic goes here + } + + protected override void OnDestroy() + { + // Custom cleanup logic goes herer + + base.OnDestroy(); + } +} +``` + ## Manual injection When dynamically adding an injectable script to a `GameObject`, you should **not** annotate the injected script with the `RequireComponent` attribute, and add the `MonoInjector` manually, like so: ```csharp - var myObject = new GameObject("MyObject"); myObject.AddComponent(); myObject.AddComponent(); @@ -110,7 +140,6 @@ public class MyComponent : MonoBehaviour { [Inject] private readonly MyService _service = default!; } - ``` ## Testing @@ -157,7 +186,11 @@ public IEnumerator My_Test_Does_Stuff() > [!NOTE] > You can also do the same in the test's `Setup` or `Teardown` stage -## Migrating from `8.0.1` to `9.0.0` +## Migrating from `8.0.1` to `9.0.0` and above + +If you want to continue using the inheritance setup, then change all base classes to be `MonoBehaviourInjected`. The new base class will always create scopes. + +If you want to migrate to the component-based injection: 1. Set up a composition root as described above. 2. Remove inheriting from the old `MonoBehaviourInjected` and similar classes From 42e0a7b42c9cb4e8ecc9b4b7969e2b0f680aacd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kurai=20Andr=C3=A1s?= Date: Fri, 14 Oct 2022 14:20:03 +0200 Subject: [PATCH 5/5] bump version --- CHANGELOG.md | 3 +++ MainProject/Directory.Build.props | 2 +- .../Injecter.Unity/Assets/Injecter.Hosting.Unity/package.json | 2 +- UnityProject/Injecter.Unity/Assets/Injecter.Unity/package.json | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6ede21..160f823 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 9.1.0 +- Add `MonoBheaviourInjected` again with new implementation. Extend docs accordingly + # 9.0.2 - Fix override handling when running the editor tool diff --git a/MainProject/Directory.Build.props b/MainProject/Directory.Build.props index c09dd66..7cc1cc0 100644 --- a/MainProject/Directory.Build.props +++ b/MainProject/Directory.Build.props @@ -3,7 +3,7 @@ 9.0 enable - 9.0.2 + 9.1.0 $(CurrentVersion) $(CurrentVersion) diff --git a/UnityProject/Injecter.Unity/Assets/Injecter.Hosting.Unity/package.json b/UnityProject/Injecter.Unity/Assets/Injecter.Hosting.Unity/package.json index da1d59e..635f41e 100644 --- a/UnityProject/Injecter.Unity/Assets/Injecter.Hosting.Unity/package.json +++ b/UnityProject/Injecter.Unity/Assets/Injecter.Hosting.Unity/package.json @@ -1,6 +1,6 @@ { "name": "com.injecter.hosting.unity", - "version": "9.0.2", + "version": "9.1.0", "displayName": "Injecter.Hosting.Unity", "license": "MIT", "licensesUrl": "https://github.com/KuraiAndras/Injecter/blob/master/LICENSE.md", diff --git a/UnityProject/Injecter.Unity/Assets/Injecter.Unity/package.json b/UnityProject/Injecter.Unity/Assets/Injecter.Unity/package.json index 47f4036..5d1e9af 100644 --- a/UnityProject/Injecter.Unity/Assets/Injecter.Unity/package.json +++ b/UnityProject/Injecter.Unity/Assets/Injecter.Unity/package.json @@ -1,6 +1,6 @@ { "name": "com.injecter.unity", - "version": "9.0.2", + "version": "9.1.0", "displayName": "Injecter.Unity", "license": "MIT", "licensesUrl": "https://github.com/KuraiAndras/Injecter/blob/master/LICENSE.md",