From 5b6bbac6894530169088a9b3f317b5de4de726a7 Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Wed, 30 Nov 2022 20:54:22 +0000 Subject: [PATCH] Add support for dependency injection --- VpSharp.Commands/CommandsExtension.cs | 49 ++++++++++++++++++- .../CommandsExtensionConfiguration.cs | 21 ++++++++ VpSharp/VpSharp.csproj | 1 + VpSharp/src/VirtualParadiseClient.cs | 7 +++ VpSharp/src/VirtualParadiseConfiguration.cs | 6 +++ 5 files changed, 82 insertions(+), 2 deletions(-) diff --git a/VpSharp.Commands/CommandsExtension.cs b/VpSharp.Commands/CommandsExtension.cs index cb5b414..a2f8d8f 100644 --- a/VpSharp.Commands/CommandsExtension.cs +++ b/VpSharp.Commands/CommandsExtension.cs @@ -1,4 +1,5 @@ using System.Reflection; +using Microsoft.Extensions.DependencyInjection; using VpSharp.ClientExtensions; using VpSharp.Commands.Attributes; using VpSharp.Entities; @@ -30,6 +31,7 @@ public sealed class CommandsExtension : VirtualParadiseClientExtension : base(client) { _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + _configuration.Services ??= client.Services; } /// @@ -37,6 +39,7 @@ public sealed class CommandsExtension : VirtualParadiseClientExtension /// /// The assembly whose command modules to register. /// + /// A module in the specified assembly does not have a public constructor, or has more than one public constructor. /// /// A command module could not be instantiated. /// @@ -48,6 +51,10 @@ public sealed class CommandsExtension : VirtualParadiseClientExtension /// A command in the specified assembly has on a parameter that is not the last in /// the parameter list. /// + /// -or- + /// + /// A module is expecting services but no was registered. + /// /// public void RegisterCommands(Assembly assembly) { @@ -66,7 +73,11 @@ public sealed class CommandsExtension : VirtualParadiseClientExtension /// Registers the commands defined in the specified type. /// /// - /// refers to a type that does not inherit . + /// refers to a type that does not inherit . + /// -or- + /// + /// does not have a public constructor, or has more than one public constructor. + /// /// /// could not be instantiated. /// @@ -78,6 +89,10 @@ public sealed class CommandsExtension : VirtualParadiseClientExtension /// A command in the specified module has on a parameter that is not the last in the /// parameter list. /// + /// -or- + /// + /// The module is expecting services but no was registered. + /// /// public void RegisterCommands() where T : CommandModule { @@ -93,6 +108,10 @@ public sealed class CommandsExtension : VirtualParadiseClientExtension /// refers to an abstract type. /// -or- /// refers to a type that does not inherit . + /// -or- + /// + /// does not have a public constructor, or has more than one public constructor. + /// /// /// could not be instantiated. /// @@ -104,6 +123,10 @@ public sealed class CommandsExtension : VirtualParadiseClientExtension /// A command in the specified module has on a parameter that is not the last in the /// parameter list. /// + /// -or- + /// + /// The module is expecting services but no was registered. + /// /// public void RegisterCommands(Type moduleType) { @@ -119,7 +142,29 @@ public sealed class CommandsExtension : VirtualParadiseClientExtension throw new ArgumentException($"Module type is not a subclass of {typeof(CommandModule)}"); } - if (Activator.CreateInstance(moduleType) is not CommandModule module) + ConstructorInfo[] constructors = moduleType.GetTypeInfo().DeclaredConstructors.Where(c => c.IsPublic).ToArray(); + if (constructors.Length != 0) + { + throw new ArgumentException( + $"Constructor for {moduleType} is not public, or {moduleType} has more than one public constructor."); + } + + ConstructorInfo constructor = constructors[0]; + ParameterInfo[] parameters = constructor.GetParameters(); + + IServiceProvider? serviceProvider = _configuration.Services; + if (parameters.Length != 0 && serviceProvider is null) + { + throw new InvalidOperationException("No ServiceProvider has been registered!"); + } + + var args = new object[parameters.Length]; + for (var index = 0; index < args.Length; index++) + { + args[index] = serviceProvider!.GetRequiredService(parameters[index].ParameterType); + } + + if (Activator.CreateInstance(moduleType, args) is not CommandModule module) { throw new TypeInitializationException(moduleType.FullName, null); } diff --git a/VpSharp.Commands/CommandsExtensionConfiguration.cs b/VpSharp.Commands/CommandsExtensionConfiguration.cs index e023c04..8e125e1 100644 --- a/VpSharp.Commands/CommandsExtensionConfiguration.cs +++ b/VpSharp.Commands/CommandsExtensionConfiguration.cs @@ -5,9 +5,30 @@ namespace VpSharp.Commands; /// public sealed class CommandsExtensionConfiguration { + /// + /// Initializes a new instance of the class. + /// + /// The configuration to copy. + public CommandsExtensionConfiguration(CommandsExtensionConfiguration? configuration = null) + { + if (configuration is null) + { + return; + } + + Prefixes = configuration.Prefixes; + Services = configuration.Services; + } + /// /// Gets or sets the prefixes to be use for commands. /// /// The command prefixes, as an array of values. public IReadOnlyList Prefixes { get; set; } = Array.Empty(); + + /// + /// Gets or sets the service provider. + /// + /// The service provider. + public IServiceProvider? Services { get; set; } } diff --git a/VpSharp/VpSharp.csproj b/VpSharp/VpSharp.csproj index b0fc31d..f6b91a5 100644 --- a/VpSharp/VpSharp.csproj +++ b/VpSharp/VpSharp.csproj @@ -57,6 +57,7 @@ + diff --git a/VpSharp/src/VirtualParadiseClient.cs b/VpSharp/src/VirtualParadiseClient.cs index 6528031..a25900c 100644 --- a/VpSharp/src/VirtualParadiseClient.cs +++ b/VpSharp/src/VirtualParadiseClient.cs @@ -46,6 +46,7 @@ public sealed partial class VirtualParadiseClient : IDisposable /// The configuration for this client. public VirtualParadiseClient(VirtualParadiseConfiguration configuration) { + Services = configuration.Services; _configuration = new VirtualParadiseConfiguration(configuration); Initialize(); } @@ -84,6 +85,12 @@ public sealed partial class VirtualParadiseClient : IDisposable get => CurrentAvatar?.Location.World; } + /// + /// Gets the service provider. + /// + /// The service provider. + public IServiceProvider? Services { get; } + /// /// Gets a read-only view of the cached worlds. /// diff --git a/VpSharp/src/VirtualParadiseConfiguration.cs b/VpSharp/src/VirtualParadiseConfiguration.cs index 558a697..ba8d405 100644 --- a/VpSharp/src/VirtualParadiseConfiguration.cs +++ b/VpSharp/src/VirtualParadiseConfiguration.cs @@ -61,6 +61,12 @@ public sealed class VirtualParadiseConfiguration /// /// The login password. public string Password { get; set; } = string.Empty; + + /// + /// Gets or sets the service provider. + /// + /// The service provider. + public IServiceProvider? Services { get; set; } /// /// Gets or sets the login username.