1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-12 21:38:38 +00:00

Core - Refactor service registration

This commit is contained in:
Robert 2023-02-04 23:25:48 +01:00
parent a1086df41a
commit f2ef657d00
18 changed files with 234 additions and 231 deletions

View File

@ -0,0 +1,66 @@
using System;
using System.Linq;
using System.Reflection;
using Artemis.Core.DryIoc.Factories;
using Artemis.Core.Services;
using Artemis.Storage;
using Artemis.Storage.Migrations.Interfaces;
using Artemis.Storage.Repositories.Interfaces;
using DryIoc;
namespace Artemis.Core.DryIoc;
/// <summary>
/// Provides an extension method to register services onto a DryIoc <see cref="IContainer"/>.
/// </summary>
public static class CoreContainerExtensions
{
/// <summary>
/// Registers core services into the container.
/// </summary>
/// <param name="container">The builder building the current container</param>
public static void RegisterCore(this IContainer container)
{
Assembly[] coreAssembly = {typeof(IArtemisService).Assembly};
Assembly[] storageAssembly = {typeof(IRepository).Assembly};
// Bind all services as singletons
container.RegisterMany(coreAssembly, type => type.IsAssignableTo<IArtemisService>(), Reuse.Singleton);
container.RegisterMany(coreAssembly, type => type.IsAssignableTo<IProtectedArtemisService>(), Reuse.Singleton, setup: Setup.With(condition: HasAccessToProtectedService));
// Bind storage
container.RegisterDelegate(() => StorageManager.CreateRepository(Constants.DataFolder), Reuse.Singleton);
container.Register<StorageMigrationService>(Reuse.Singleton);
container.RegisterMany(storageAssembly, type => type.IsAssignableTo<IRepository>(), Reuse.Singleton);
// Bind migrations
container.RegisterMany(storageAssembly, type => type.IsAssignableTo<IStorageMigration>(), Reuse.Singleton, nonPublicServiceTypes: true);
container.Register<IPluginSettingsFactory, PluginSettingsFactory>(Reuse.Singleton);
container.Register(made: Made.Of(_ => ServiceInfo.Of<IPluginSettingsFactory>(), f => f.CreatePluginSettings(Arg.Index<Type>(0)), r => r.Parent.ImplementationType));
container.Register<ILoggerFactory, LoggerFactory>(Reuse.Singleton);
container.Register(made: Made.Of(_ => ServiceInfo.Of<ILoggerFactory>(), f => f.CreateLogger(Arg.Index<Type>(0)), r => r.Parent.ImplementationType));
}
/// <summary>
/// Registers plugin services into the container, this is typically a child container.
/// </summary>
/// <param name="container">The builder building the current container</param>
/// <param name="plugin">The plugin to register</param>
public static void RegisterPlugin(this IContainer container, Plugin plugin)
{
container.RegisterInstance(plugin, setup: Setup.With(preventDisposal: true));
// Bind plugin service interfaces, DryIoc expects at least one match when calling RegisterMany so ensure there is something to register first
if (plugin.Assembly != null && plugin.Assembly.GetTypes().Any(t => t.IsAssignableTo<IPluginService>()))
container.RegisterMany(new[] {plugin.Assembly}, type => type.IsAssignableTo<IPluginService>(), Reuse.Singleton, ifAlreadyRegistered: IfAlreadyRegistered.Keep);
}
private static bool HasAccessToProtectedService(Request request)
{
// Plugin assembly locations may not be set for some reason, that case it's also not allowed >:(
return request.Parent.ImplementationType != null &&
!string.IsNullOrWhiteSpace(request.Parent.ImplementationType.Assembly.Location) &&
!request.Parent.ImplementationType.Assembly.Location.StartsWith(Constants.PluginsFolder);
}
}

View File

@ -1,48 +0,0 @@
using System;
using System.Reflection;
using Artemis.Core.DryIoc.Factories;
using Artemis.Core.Services;
using Artemis.Storage;
using Artemis.Storage.Migrations.Interfaces;
using Artemis.Storage.Repositories.Interfaces;
using DryIoc;
namespace Artemis.Core.DryIoc;
/// <summary>
/// The main <see cref="IModule" /> of the Artemis Core that binds all services
/// </summary>
public class CoreModule : IModule
{
/// <inheritdoc />
public void Load(IRegistrator builder)
{
Assembly coreAssembly = typeof(IArtemisService).Assembly;
Assembly storageAssembly = typeof(IRepository).Assembly;
// Bind all services as singletons
builder.RegisterMany(new[] {coreAssembly}, type => type.IsAssignableTo<IArtemisService>(), Reuse.Singleton);
builder.RegisterMany(new[] {coreAssembly}, type => type.IsAssignableTo<IProtectedArtemisService>(), Reuse.Singleton, setup: Setup.With(condition: HasAccessToProtectedService));
// Bind storage
builder.RegisterDelegate(() => StorageManager.CreateRepository(Constants.DataFolder), Reuse.Singleton);
builder.Register<StorageMigrationService>(Reuse.Singleton);
builder.RegisterMany(new[] {storageAssembly}, type => type.IsAssignableTo<IRepository>(), Reuse.Singleton);
// Bind migrations
builder.RegisterMany(new[] { storageAssembly }, type => type.IsAssignableTo<IStorageMigration>(), Reuse.Singleton, nonPublicServiceTypes: true);
builder.Register<IPluginSettingsFactory, PluginSettingsFactory>(Reuse.Singleton);
builder.Register(made: Made.Of(_ => ServiceInfo.Of<IPluginSettingsFactory>(), factory => factory.CreatePluginSettings(Arg.Index<Type>(0)), r => r.Parent.ImplementationType));
builder.Register<ILoggerFactory, LoggerFactory>(Reuse.Singleton);
builder.Register(made: Made.Of(_ => ServiceInfo.Of<ILoggerFactory>(), f => f.CreateLogger(Arg.Index<Type>(0)), r => r.Parent.ImplementationType));
}
private bool HasAccessToProtectedService(Request request)
{
// Plugin assembly locations may not be set for some reason, that case it's also not allowed >:(
return request.Parent.ImplementationType != null &&
!string.IsNullOrWhiteSpace(request.Parent.ImplementationType.Assembly.Location) &&
!request.Parent.ImplementationType.Assembly.Location.StartsWith(Constants.PluginsFolder);
}
}

View File

@ -1,15 +0,0 @@
using DryIoc;
namespace Artemis.Core.DryIoc;
/**
* Represents a service module.
*/
public interface IModule
{
/// <summary>
/// Registers the services provided by the module.
/// </summary>
/// <param name="builder">The builder to register the services with.</param>
void Load(IRegistrator builder);
}

View File

@ -1,26 +0,0 @@
using System;
using System.Linq;
using Artemis.Core.Services;
using DryIoc;
namespace Artemis.Core.DryIoc;
internal class PluginModule : IModule
{
public PluginModule(Plugin plugin)
{
Plugin = plugin ?? throw new ArgumentNullException(nameof(plugin));
}
public Plugin Plugin { get; }
/// <inheritdoc />
public void Load(IRegistrator builder)
{
builder.RegisterInstance(Plugin, setup: Setup.With(preventDisposal: true));
// Bind plugin service interfaces, DryIoc expects at least one match when calling RegisterMany so ensure there is something to register first
if (Plugin.Assembly != null && Plugin.Assembly.GetTypes().Any(t => t.IsAssignableTo<IPluginService>()))
builder.RegisterMany(new[] {Plugin.Assembly}, type => type.IsAssignableTo<IPluginService>(), Reuse.Singleton, ifAlreadyRegistered: IfAlreadyRegistered.Keep);
}
}

View File

@ -435,7 +435,7 @@ internal class PluginManagementService : IPluginManagementService
plugin.Container = _container.CreateChild(newRules: _container.Rules.WithConcreteTypeDynamicRegistrations());
try
{
new PluginModule(plugin).Load(plugin.Container);
plugin.Container.RegisterPlugin(plugin);
}
catch (Exception e)
{

View File

@ -1,4 +1,5 @@
using Artemis.Core.Services;
using Artemis.UI.Linux.DryIoc;
using Artemis.UI.Linux.Providers.Input;
using Avalonia;
using Avalonia.Controls;
@ -17,7 +18,7 @@ public class App : Application
public override void Initialize()
{
_container = ArtemisBootstrapper.Bootstrap(this);
_container = ArtemisBootstrapper.Bootstrap(this, c => c.RegisterProviders());
Program.CreateLogger(_container);
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
AvaloniaXamlLoader.Load(this);

View File

@ -0,0 +1,20 @@
using Artemis.Core.Services;
using Artemis.UI.Linux.Providers.Input;
using DryIoc;
namespace Artemis.UI.Linux.DryIoc;
/// <summary>
/// Provides an extension method to register services onto a DryIoc <see cref="IContainer"/>.
/// </summary>
public static class UIContainerExtensions
{
/// <summary>
/// Registers providers into the container.
/// </summary>
/// <param name="container">The builder building the current container</param>
public static void RegisterProviders(this IContainer container)
{
container.Register<InputProvider, LinuxInputProvider>(serviceKey: LinuxInputProvider.Id);
}
}

View File

@ -1,17 +0,0 @@
using Artemis.Core.DryIoc;
using Artemis.Core.Services;
using Artemis.UI.Linux.Providers.Input;
using DryIoc;
namespace Artemis.UI.Linux.DryIoc;
public class LinuxModule : IModule
{
/// <inheritdoc />
public void Load(IRegistrator builder)
{
builder.Register<InputProvider, LinuxInputProvider>(serviceKey: LinuxInputProvider.Id);
}
}

View File

@ -0,0 +1,21 @@
using System.Reflection;
using Artemis.UI.Shared.Services;
using DryIoc;
namespace Artemis.UI.Shared.DryIoc;
/// <summary>
/// Provides an extension method to register services onto a DryIoc <see cref="IContainer"/>.
/// </summary>
public static class UIContainerExtensions
{
/// <summary>
/// Registers shared UI services into the container.
/// </summary>
/// <param name="container">The builder building the current container</param>
public static void RegisterSharedUI(this IContainer container)
{
Assembly artemisShared = typeof(IArtemisSharedUIService).GetAssembly();
container.RegisterMany(new[] { artemisShared }, type => type.IsAssignableTo<IArtemisSharedUIService>(), Reuse.Singleton);
}
}

View File

@ -1,19 +0,0 @@
using System.Reflection;
using Artemis.Core.DryIoc;
using Artemis.UI.Shared.Services;
using DryIoc;
namespace Artemis.UI.Shared.DryIoc;
/// <summary>
/// The main <see cref="IModule" /> of the Artemis Shared UI toolkit that binds all services
/// </summary>
public class SharedUIModule : IModule
{
/// <inheritdoc />
public void Load(IRegistrator builder)
{
Assembly artemisShared = typeof(IArtemisSharedUIService).GetAssembly();
builder.RegisterMany(new[] { artemisShared }, type => type.IsAssignableTo<IArtemisSharedUIService>(), Reuse.Singleton);
}
}

View File

@ -33,7 +33,7 @@ public class App : Application
Environment.Exit(1);
}
_container = ArtemisBootstrapper.Bootstrap(this, new WindowsModule());
_container = ArtemisBootstrapper.Bootstrap(this, c => c.RegisterProviders());
Program.CreateLogger(_container);
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
AvaloniaXamlLoader.Load(this);

View File

@ -0,0 +1,27 @@
using Artemis.Core.Providers;
using Artemis.Core.Services;
using Artemis.UI.Shared.Providers;
using Artemis.UI.Windows.Providers;
using Artemis.UI.Windows.Providers.Input;
using DryIoc;
namespace Artemis.UI.Windows.DryIoc;
/// <summary>
/// Provides an extension method to register services onto a DryIoc <see cref="IContainer"/>.
/// </summary>
public static class UIContainerExtensions
{
/// <summary>
/// Registers providers into the container.
/// </summary>
/// <param name="container">The builder building the current container</param>
public static void RegisterProviders(this IContainer container)
{
container.Register<ICursorProvider, CursorProvider>(Reuse.Singleton);
container.Register<IGraphicsContextProvider, GraphicsContextProvider>(Reuse.Singleton);
container.Register<IUpdateProvider, UpdateProvider>(Reuse.Singleton);
container.Register<IAutoRunProvider, AutoRunProvider>();
container.Register<InputProvider, WindowsInputProvider>(serviceKey: WindowsInputProvider.Id);
}
}

View File

@ -1,22 +0,0 @@
using Artemis.Core.DryIoc;
using Artemis.Core.Providers;
using Artemis.Core.Services;
using Artemis.UI.Shared.Providers;
using Artemis.UI.Windows.Providers;
using Artemis.UI.Windows.Providers.Input;
using DryIoc;
namespace Artemis.UI.Windows.DryIoc;
public class WindowsModule : IModule
{
/// <inheritdoc />
public void Load(IRegistrator builder)
{
builder.Register<ICursorProvider, CursorProvider>(Reuse.Singleton);
builder.Register<IGraphicsContextProvider, GraphicsContextProvider>(Reuse.Singleton);
builder.Register<IUpdateProvider, UpdateProvider>(Reuse.Singleton);
builder.Register<IAutoRunProvider, AutoRunProvider>();
builder.Register<InputProvider, WindowsInputProvider>(serviceKey: WindowsInputProvider.Id);
}
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reactive;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.DryIoc;
using Artemis.UI.DryIoc;
@ -11,6 +12,8 @@ using Artemis.UI.Shared.DataModelPicker;
using Artemis.UI.Shared.DryIoc;
using Artemis.UI.Shared.Services;
using Artemis.VisualScripting.DryIoc;
using Artemis.WebClient.Updating;
using Artemis.WebClient.Updating.DryIoc;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
@ -25,7 +28,7 @@ public static class ArtemisBootstrapper
private static Container? _container;
private static Application? _application;
public static IContainer Bootstrap(Application application, params IModule[] modules)
public static IContainer Bootstrap(Application application, Action<IContainer>? configureServices = null)
{
if (_application != null || _container != null)
throw new ArtemisUIException("UI already bootstrapped");
@ -33,18 +36,19 @@ public static class ArtemisBootstrapper
Utilities.PrepareFirstLaunch();
_application = application;
_container = new Container(rules => rules.WithConcreteTypeDynamicRegistrations()
.WithoutThrowOnRegisteringDisposableTransient());
_container = new Container(rules => rules
.WithMicrosoftDependencyInjectionRules()
.WithConcreteTypeDynamicRegistrations()
.WithoutThrowOnRegisteringDisposableTransient());
new CoreModule().Load(_container);
new UIModule().Load(_container);
new SharedUIModule().Load(_container);
new NoStringDryIocModule().Load(_container);
foreach (IModule module in modules)
module.Load(_container);
_container.RegisterCore();
_container.RegisterUI();
_container.RegisterSharedUI();
_container.RegisterUpdatingClient();
_container.RegisterNoStringEvaluating();
configureServices?.Invoke(_container);
_container.UseDryIocDependencyResolver();
return _container;
}

View File

@ -0,0 +1,42 @@
using System.Reflection;
using Artemis.UI.DryIoc.Factories;
using Artemis.UI.DryIoc.InstanceProviders;
using Artemis.UI.Screens;
using Artemis.UI.Screens.VisualScripting;
using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services.NodeEditor;
using Artemis.UI.Shared.Services.ProfileEditor;
using Avalonia.Platform;
using Avalonia.Shared.PlatformSupport;
using DryIoc;
namespace Artemis.UI.DryIoc;
/// <summary>
/// Provides an extension method to register services onto a DryIoc <see cref="IContainer"/>.
/// </summary>
public static class UIContainerExtensions
{
/// <summary>
/// Registers UI services into the container.
/// </summary>
/// <param name="container">The builder building the current container</param>
public static void RegisterUI(this IContainer container)
{
Assembly[] thisAssembly = {typeof(UIContainerExtensions).Assembly};
container.RegisterInstance(new AssetLoader(), IfAlreadyRegistered.Throw);
container.Register<IAssetLoader, AssetLoader>(Reuse.Singleton);
container.RegisterMany(thisAssembly, type => type.IsAssignableTo<ViewModelBase>());
container.RegisterMany(thisAssembly, type => type.IsAssignableTo<MainScreenViewModel>(), ifAlreadyRegistered: IfAlreadyRegistered.Replace);
container.RegisterMany(thisAssembly, type => type.IsAssignableTo<IToolViewModel>() && type.IsInterface);
container.RegisterMany(thisAssembly, type => type.IsAssignableTo<IVmFactory>() && type != typeof(PropertyVmFactory));
container.Register<NodeScriptWindowViewModelBase, NodeScriptWindowViewModel>(Reuse.Singleton);
container.Register<IPropertyVmFactory, PropertyVmFactory>(Reuse.Singleton);
container.RegisterMany(thisAssembly, type => type.IsAssignableTo<IArtemisUIService>(), Reuse.Singleton);
}
}

View File

@ -1,36 +0,0 @@
using System.Reflection;
using Artemis.Core.DryIoc;
using Artemis.UI.DryIoc.Factories;
using Artemis.UI.DryIoc.InstanceProviders;
using Artemis.UI.Screens;
using Artemis.UI.Screens.VisualScripting;
using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services.NodeEditor;
using Artemis.UI.Shared.Services.ProfileEditor;
using Avalonia.Platform;
using Avalonia.Shared.PlatformSupport;
using DryIoc;
namespace Artemis.UI.DryIoc;
public class UIModule : IModule
{
public void Load(IRegistrator builder)
{
Assembly thisAssembly = typeof(UIModule).Assembly;
builder.RegisterInstance(new AssetLoader(), IfAlreadyRegistered.Throw);
builder.Register<IAssetLoader, AssetLoader>(Reuse.Singleton);
builder.RegisterMany(new[] { thisAssembly }, type => type.IsAssignableTo<ViewModelBase>());
builder.RegisterMany(new[] { thisAssembly }, type => type.IsAssignableTo<MainScreenViewModel>(), ifAlreadyRegistered: IfAlreadyRegistered.Replace);
builder.RegisterMany(new[] { thisAssembly }, type => type.IsAssignableTo<IToolViewModel>() && type.IsInterface);
builder.RegisterMany(new[] { thisAssembly }, type => type.IsAssignableTo<IVmFactory>() && type != typeof(PropertyVmFactory));
builder.Register<NodeScriptWindowViewModelBase, NodeScriptWindowViewModel>(Reuse.Singleton);
builder.Register<IPropertyVmFactory, PropertyVmFactory>(Reuse.Singleton);
builder.RegisterMany(new[] { thisAssembly }, type => type.IsAssignableTo<IArtemisUIService>(), Reuse.Singleton);
}
}

View File

@ -0,0 +1,40 @@
using DryIoc;
using Microsoft.Extensions.ObjectPool;
using NoStringEvaluating;
using NoStringEvaluating.Contract;
using NoStringEvaluating.Models.Values;
using NoStringEvaluating.Services.Cache;
using NoStringEvaluating.Services.Checking;
using NoStringEvaluating.Services.Parsing;
using NoStringEvaluating.Services.Parsing.NodeReaders;
namespace Artemis.VisualScripting.DryIoc;
/// <summary>
/// Provides an extension method to register services onto a DryIoc <see cref="IContainer"/>.
/// </summary>
public static class UIContainerExtensions
{
/// <summary>
/// Registers NoStringEvaluating services into the container.
/// </summary>
/// <param name="container">The builder building the current container</param>
public static void RegisterNoStringEvaluating(this IContainer container)
{
// Pooling
container.RegisterInstance(ObjectPool.Create<Stack<InternalEvaluatorValue>>());
container.RegisterInstance(ObjectPool.Create<List<InternalEvaluatorValue>>());
container.RegisterInstance(ObjectPool.Create<ExtraTypeIdContainer>());
// Parser
container.Register<IFormulaCache, FormulaCache>(Reuse.Singleton);
container.Register<IFunctionReader, FunctionReader>(Reuse.Singleton);
container.Register<IFormulaParser, FormulaParser>(Reuse.Singleton);
// Checker
container.Register<IFormulaChecker, FormulaChecker>(Reuse.Singleton);
// Evaluator
container.Register<INoStringEvaluator, NoStringEvaluator>(Reuse.Singleton);
}
}

View File

@ -1,35 +0,0 @@
using Artemis.Core.DryIoc;
using DryIoc;
using Microsoft.Extensions.ObjectPool;
using NoStringEvaluating;
using NoStringEvaluating.Contract;
using NoStringEvaluating.Models.Values;
using NoStringEvaluating.Services.Cache;
using NoStringEvaluating.Services.Checking;
using NoStringEvaluating.Services.Parsing;
using NoStringEvaluating.Services.Parsing.NodeReaders;
namespace Artemis.VisualScripting.DryIoc;
public class NoStringDryIocModule : IModule
{
/// <inheritdoc />
public void Load(IRegistrator builder)
{
// Pooling
builder.RegisterInstance(ObjectPool.Create<Stack<InternalEvaluatorValue>>());
builder.RegisterInstance(ObjectPool.Create<List<InternalEvaluatorValue>>());
builder.RegisterInstance(ObjectPool.Create<ExtraTypeIdContainer>());
// Parser
builder.Register<IFormulaCache, FormulaCache>(Reuse.Singleton);
builder.Register<IFunctionReader, FunctionReader>(Reuse.Singleton);
builder.Register<IFormulaParser, FormulaParser>(Reuse.Singleton);
// Checker
builder.Register<IFormulaChecker, FormulaChecker>(Reuse.Singleton);
// Evaluator
builder.Register<INoStringEvaluator, NoStringEvaluator>(Reuse.Singleton);
}
}