Skip to content

Commit

Permalink
Merge pull request #100 from KuraiAndras/feature/add-inhertiance-inje…
Browse files Browse the repository at this point in the history
…ction

9.1.0
  • Loading branch information
KuraiAndras authored Oct 14, 2022
2 parents b6eebfa + 42e0a7b commit 2e2fa8e
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 13 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
49 changes: 41 additions & 8 deletions Documentation/documentation/injecter.unity.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand All @@ -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<TestComponent>();
myObject.AddComponent<MonoInjector>();
Expand All @@ -110,7 +140,6 @@ public class MyComponent : MonoBehaviour
{
[Inject] private readonly MyService _service = default!;
}

```

## Testing
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion MainProject/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<LangVersion>9.0</LangVersion>
<Nullable>enable</Nullable>

<CurrentVersion>9.0.2</CurrentVersion>
<CurrentVersion>9.1.0</CurrentVersion>

<Version>$(CurrentVersion)</Version>
<PakcageVersion>$(CurrentVersion)</PakcageVersion>
Expand Down
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Microsoft.Extensions.DependencyInjection;
using UnityEngine;

namespace Injecter.Unity
{
/// <summary>
/// Inheriting from this class will inject everything marked with the <see cref="InjectAttribute"/>
/// </summary>
public abstract class MonoBehaviourInjected : MonoBehaviour
{
[Inject] private IScopeStore _store;

protected IServiceScope Scope { get; private set; }

/// <summary>
/// Call the base function before accessing injected members
/// </summary>
[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<IInjecter>().InjectIntoType(this, true);

/// <summary>
/// Call the base function after accessing injected members
/// </summary>
[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;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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",
Expand Down

0 comments on commit 2e2fa8e

Please sign in to comment.