Make DI services scoped
This commit is contained in:
@@ -14,18 +14,19 @@ public class InjectAttribute : Attribute;
|
||||
public partial class DiAutoload : Node
|
||||
{
|
||||
private ServiceProvider? _serviceProvider;
|
||||
private IServiceScope? _currentScope;
|
||||
|
||||
public override void _EnterTree()
|
||||
{
|
||||
GD.Print("[Injector] _EnterTree called");
|
||||
var services = new ServiceCollection();
|
||||
// Register services here
|
||||
services.AddSingleton<BuildService>();
|
||||
services.AddSingleton<RunService>();
|
||||
services.AddSingleton<IdeFileExternalChangeHandler>();
|
||||
services.AddSingleton<IdeFileSavedToDiskHandler>();
|
||||
services.AddSingleton<IdeFileWatcher>();
|
||||
services.AddSingleton<IdeOpenTabsFileManager>();
|
||||
services.AddScoped<BuildService>();
|
||||
services.AddScoped<RunService>();
|
||||
services.AddScoped<IdeFileExternalChangeHandler>();
|
||||
services.AddScoped<IdeFileSavedToDiskHandler>();
|
||||
services.AddScoped<IdeFileWatcher>();
|
||||
services.AddScoped<IdeOpenTabsFileManager>();
|
||||
|
||||
_serviceProvider = services.BuildServiceProvider();
|
||||
GetTree().NodeAdded += OnNodeAdded;
|
||||
@@ -37,6 +38,13 @@ public partial class DiAutoload : Node
|
||||
|
||||
}
|
||||
|
||||
/// The solution has changed, so reset the scope to get new services
|
||||
public void ResetScope()
|
||||
{
|
||||
_currentScope?.Dispose();
|
||||
_currentScope = _serviceProvider!.CreateScope();
|
||||
}
|
||||
|
||||
private void OnNodeAdded(Node node)
|
||||
{
|
||||
// Inject dependencies into every new node
|
||||
@@ -52,11 +60,18 @@ public partial class DiAutoload : Node
|
||||
{
|
||||
if (Attribute.IsDefined(field, typeof(InjectAttribute)))
|
||||
{
|
||||
var service = _serviceProvider!.GetService(field.FieldType);
|
||||
if (_currentScope is null)
|
||||
{
|
||||
GD.PrintErr("[Injector] _currentScope was null when attempting to resolve service");
|
||||
GetTree().Quit();
|
||||
return;
|
||||
}
|
||||
var service = _currentScope!.ServiceProvider.GetService(field.FieldType);
|
||||
if (service is null)
|
||||
{
|
||||
GD.PrintErr($"[Injector] No service registered for {field.FieldType}");
|
||||
GetTree().Quit();
|
||||
return;
|
||||
}
|
||||
|
||||
field.SetValue(target, service);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using Godot;
|
||||
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
|
||||
using SharpIDE.Godot.Features.Build;
|
||||
using SharpIDE.Godot.Features.Debug_;
|
||||
using SharpIDE.Godot.Features.IdeDiagnostics;
|
||||
using SharpIDE.Godot.Features.Problems;
|
||||
using SharpIDE.Godot.Features.Run;
|
||||
|
||||
namespace SharpIDE.Godot.Features.BottomPanel;
|
||||
|
||||
@@ -17,9 +20,9 @@ public partial class BottomPanelManager : Panel
|
||||
}
|
||||
}
|
||||
|
||||
private Control _runPanel = null!;
|
||||
private Control _debugPanel = null!;
|
||||
private Control _buildPanel = null!;
|
||||
private RunPanel _runPanel = null!;
|
||||
private DebugPanel _debugPanel = null!;
|
||||
private BuildPanel _buildPanel = null!;
|
||||
private ProblemsPanel _problemsPanel = null!;
|
||||
private IdeDiagnosticsPanel _ideDiagnosticsPanel = null!;
|
||||
|
||||
@@ -27,9 +30,9 @@ public partial class BottomPanelManager : Panel
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_runPanel = GetNode<Control>("%RunPanel");
|
||||
_debugPanel = GetNode<Control>("%DebugPanel");
|
||||
_buildPanel = GetNode<Control>("%BuildPanel");
|
||||
_runPanel = GetNode<RunPanel>("%RunPanel");
|
||||
_debugPanel = GetNode<DebugPanel>("%DebugPanel");
|
||||
_buildPanel = GetNode<BuildPanel>("%BuildPanel");
|
||||
_problemsPanel = GetNode<ProblemsPanel>("%ProblemsPanel");
|
||||
_ideDiagnosticsPanel = GetNode<IdeDiagnosticsPanel>("%IdeDiagnosticsPanel");
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ public partial class IdeRoot : Control
|
||||
[Inject] private readonly IdeFileExternalChangeHandler _fileExternalChangeHandler = null!;
|
||||
[Inject] private readonly IdeFileWatcher _fileWatcher = null!;
|
||||
[Inject] private readonly BuildService _buildService = null!;
|
||||
[Inject] private readonly IdeOpenTabsFileManager _openTabsFileManager = null!;
|
||||
|
||||
public override void _EnterTree()
|
||||
{
|
||||
@@ -52,7 +53,8 @@ public partial class IdeRoot : Control
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
_fileWatcher.Dispose();
|
||||
_fileWatcher?.Dispose();
|
||||
GetTree().GetRoot().FocusExited -= OnFocusExited;
|
||||
}
|
||||
|
||||
public override void _Ready()
|
||||
@@ -80,8 +82,15 @@ public partial class IdeRoot : Control
|
||||
_cleanSlnButton.Pressed += OnCleanSlnButtonPressed;
|
||||
_restoreSlnButton.Pressed += OnRestoreSlnButtonPressed;
|
||||
GodotGlobalEvents.Instance.BottomPanelVisibilityChangeRequested.Subscribe(async show => await this.InvokeAsync(() => _invertedVSplitContainer.InvertedSetCollapsed(!show)));
|
||||
GetTree().GetRoot().FocusExited += OnFocusExited;
|
||||
_nodeReadyTcs.SetResult();
|
||||
}
|
||||
|
||||
// TODO: Problematic, as this is called even when the focus shifts to an embedded subwindow, such as a popup
|
||||
private void OnFocusExited()
|
||||
{
|
||||
_ = Task.GodotRun(async () => await _openTabsFileManager.SaveAllOpenFilesAsync());
|
||||
}
|
||||
|
||||
private void OnRunMenuButtonPressed()
|
||||
{
|
||||
|
||||
@@ -21,8 +21,6 @@ public partial class IdeWindow : Control
|
||||
private IdeRoot? _ideRoot;
|
||||
private SlnPicker? _slnPicker;
|
||||
|
||||
[Inject] private readonly IdeOpenTabsFileManager _openTabsFileManager = null!;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
GD.Print("IdeWindow _Ready called");
|
||||
@@ -31,7 +29,6 @@ public partial class IdeWindow : Control
|
||||
MSBuildLocator.RegisterDefaults();
|
||||
GodotServiceDefaults.AddServiceDefaults();
|
||||
Singletons.AppState = AppStateLoader.LoadAppStateFromConfigFile();
|
||||
GetWindow().FocusExited += OnFocusExited;
|
||||
//GetWindow().SetMinSize(new Vector2I(1152, 648));
|
||||
Callable.From(() => PickSolution(true)).CallDeferred();
|
||||
}
|
||||
@@ -47,12 +44,6 @@ public partial class IdeWindow : Control
|
||||
// PrintOrphanNodes();
|
||||
}
|
||||
|
||||
// TODO: Problematic, as this is called even when the focus shifts to an embedded subwindow, such as a popup
|
||||
private void OnFocusExited()
|
||||
{
|
||||
_ = Task.GodotRun(async () => await _openTabsFileManager.SaveAllOpenFilesAsync());
|
||||
}
|
||||
|
||||
public void PickSolution(bool fullscreen = false)
|
||||
{
|
||||
if (_slnPicker is not null) throw new InvalidOperationException("Solution picker is already active");
|
||||
@@ -109,7 +100,8 @@ public partial class IdeWindow : Control
|
||||
{
|
||||
GetWindow().Mode = Window.ModeEnum.Maximized;
|
||||
}
|
||||
_ideRoot = ideRoot;
|
||||
_ideRoot = ideRoot; // This has no DI services, until it is added to the scene tree
|
||||
GetNode<DiAutoload>("/root/DiAutoload").ResetScope();
|
||||
AddChild(ideRoot);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user