Merge pull request #520 from james7132/property-injection

Conflicts:
	docs/guides/samples/dependency_module.cs
	src/Discord.Net.Commands/Utilities/ReflectionUtils.cs
This commit is contained in:
Christopher F
2017-02-23 15:51:24 -05:00
4 changed files with 157 additions and 114 deletions

View File

@@ -166,6 +166,11 @@ that are placed in the constructor must be injected into an
@Discord.Commands.IDependencyMap. Alternatively, you may accept an
IDependencyMap as an argument and extract services yourself.
### Module Properties
Modules with public settable properties will have them injected after module
construction.
### Module Groups
Module Groups allow you to create a module where commands are prefixed.
@@ -212,10 +217,16 @@ Your modules will automatically be loaded with this dependency map.
In the constructor of your module, any parameters will be filled in by
the @Discord.Commands.IDependencyMap you pass into `LoadAssembly`.
Any publicly settable properties will also be filled in the same manner.
>[!NOTE]
> Annotating a property with the [DontInject] attribute will prevent it from
being injected.
>[!NOTE]
>If you accept `CommandService` or `IDependencyMap` as a parameter in
your constructor, these parameters will be filled by the
CommandService the module was loaded from, and the DependencyMap passed
your constructor or as an injectable property, these entries will be filled
by the CommandService the module was loaded from, and the DependencyMap passed
into it, respectively.
[!code-csharp[DependencyMap in Modules](samples/dependency_module.cs)]

View File

@@ -6,6 +6,7 @@ public class ModuleA : ModuleBase
{
private readonly DatabaseService _database;
// Dependencies can be injected via the constructor
public ModuleA(DatabaseService database)
{
_database = database;
@@ -20,12 +21,20 @@ public class ModuleA : ModuleBase
public class ModuleB
{
private CommandService _commands;
private NotificationService _notification;
public ModuleB(CommandService commands, NotificationService notifications)
// Public settable properties will be injected
public AnnounceService { get; set; }
// Public properties without setters will not
public CommandService Commands { get; }
// Public properties annotated with [DontInject] will not
[DontInject]
public NotificationService { get; set; }
public ModuleB(CommandService commands)
{
_commands = commands;
_notification = notifications;
Commands = commands;
}
}

View File

@@ -0,0 +1,9 @@
using System;
namespace Discord.Commands {
[AttributeUsage(AttributeTargets.Property)]
public class DontInjectAttribute : Attribute {
}
}

View File

@@ -19,6 +19,9 @@ namespace Discord.Commands
var constructor = constructors[0];
System.Reflection.ParameterInfo[] parameters = constructor.GetParameters();
System.Reflection.PropertyInfo[] properties = typeInfo.DeclaredProperties
.Where(p => p.SetMethod?.IsPublic == true && p.GetCustomAttribute<DontInjectAttribute>() == null)
.ToArray();
return (map) =>
{
@@ -27,29 +30,40 @@ namespace Discord.Commands
for (int i = 0; i < parameters.Length; i++)
{
var parameter = parameters[i];
object arg;
if (map == null || !map.TryGet(parameter.ParameterType, out arg))
{
if (parameter.ParameterType == typeof(CommandService))
arg = service;
else if (parameter.ParameterType == typeof(IDependencyMap))
arg = map;
else
throw new InvalidOperationException($"Failed to create \"{typeInfo.FullName}\", dependency \"{parameter.ParameterType.Name}\" was not found.");
}
args[i] = arg;
args[i] = GetMember(parameter.ParameterType, map, service, typeInfo);
}
T obj;
try
{
T instance = (T)constructor.Invoke(args);
return instance;
obj = (T)constructor.Invoke(args);
}
catch (Exception ex)
{
throw new Exception($"Failed to create \"{typeInfo.FullName}\"", ex);
}
foreach(var property in properties)
{
property.SetValue(obj, GetMember(property.PropertyType, map, service, typeInfo));
}
return obj;
};
}
internal static object GetMember(Type targetType, IDependencyMap map, CommandService service, TypeInfo baseType)
{
object arg;
if (map == null || !map.TryGet(targetType, out arg))
{
if (targetType == typeof(CommandService))
arg = service;
else if (targetType == typeof(IDependencyMap))
arg = map;
else
throw new InvalidOperationException($"Failed to create \"{baseType.FullName}\", dependency \"{targetType.Name}\" was not found.");
}
return arg;
}
}
}