【问题标题】:Dependency Injection Into {get; set;} Property依赖注入到 {get;设置;} 属性
【发布时间】:2021-03-17 04:31:39
【问题描述】:

我想知道如何设置我的依赖注入来将依赖注入到具有公共 getter 和 setter ({get; set}) 的属性中。

所以,一个例子是:

namespace Dexter.Services {

    public class CommandHandlerService : InitializableModule {

        public CommandService CommandService { get; set; }

    }

}

使用以下依赖注入器:

namespace Dexter {

    public static class InitializeDependencies {

        public static async Task Main() {

            ServiceCollection ServiceCollection = new();

            CommandService CommandService = new();
            ServiceCollection.AddSingleton(CommandService);

            Assembly.GetExecutingAssembly().GetTypes()
                    .Where(Type => Type.IsSubclassOf(typeof(InitializableModule)) && !Type.IsAbstract)
                    .ToList().ForEach(
                Type => ServiceCollection.TryAddSingleton(Type)
            );


            ServiceProvider = ServiceCollection.BuildServiceProvider();

            // Initialization stuff.
        }

    }

}

在本例中,我希望 CommandService 自动注入到属性中。

我知道这是可能的,因为 Discord.NET 能够做到这一点,而且我愿意坚持使用相同的代码风格。

(Discord.NET:https://docs.stillu.cc/guides/commands/dependency-injection.html

谢谢!

【问题讨论】:

  • I know this is possible 不,这是不可能的。不适用于 Microsoft.Extensions.DependencyInjection。您的链接指向第三方 DI 库,该库使用自己的服务注入一些特定的类和属性
  • 我怀疑_commands.AddModulesAsync 检查所有模块类型的公共属性并为创建类型设置任何公共属性的类型注册工厂方法。
  • 那么实现这一目标的最佳方法是什么?
  • 你必须做 Discord.NET 所做的事情——编写你自己的代码来检查一个类型,找到可以通过反射注入的属性并设置它们。使用使用 Func 的 TryAddSingleton 重载
  • 使用构造函数注入可能更容易并且避免产生半构建的对象。

标签: c# .net .net-core dependency-injection .net-5


【解决方案1】:

无需使用Quickwire NuGet 包替换默认 DI 容器 (IServiceProvider) 即可完成此操作。

只需使用[RegisterService] 属性装饰您的服务并将[InjectService] 添加到属性。不需要接口。

[RegisterService(ServiceLifetime.Singleton)]
public class CommandHandlerService {

    [InjectService]
    public CommandService CommandService { get; set; }

}

现在从你的主函数中调用ScanCurrentAssembly

public static async Task Main() {

    ServiceCollection ServiceCollection = new();

    ServiceCollection.ScanCurrentAssembly();

    ServiceProvider = ServiceCollection.BuildServiceProvider();

    // Initialization stuff.
}

在幕后,ScanCurrentAssembly 进行所有必要的连接以解决依赖关系、实例化类并将其注入属性。

【讨论】:

  • 这是一个比我所说的更清晰的答案。我会确保勾选它!
【解决方案2】:

对于任何好奇的人,根据 Panagiotis 的建议,解决此问题的方法是创建自己的依赖注入。因此,我编写了一个小方法来循环提供程序中的所有服务,并将公共属性附加到它。它可能有错误!特别是关于范围服务,我没有写它来支持它,但这对于希望获得类似结果的人来说应该是一个很好的起点!

public static object SetClassParameters(this object newClass, IServiceScope scope, IServiceProvider sp)
{
    newClass.GetType().GetProperties().ToList().ForEach(property =>
    {
        if (property.PropertyType == typeof(IServiceProvider))
            property.SetValue(newClass, sp);
        else
        {
            object service = scope.ServiceProvider.GetService(property.PropertyType);

            if (service != null)
            {
                property.SetValue(newClass, service);
            }
        }
    });

    return newClass;
}

您可以使用如下方法将依赖项注入到类中。例如,我希望将它们注入到扩展我创建的抽象“事件”类的类中。这可以在下面看到:

using (var scope = serviceProvider.CreateScope()) {
    GetEvents().ForEach(
        type => serviceProvider.GetRequiredService(type).SetClassParameters(scope, serviceProvider)
    );
}

其中GetEvents() 是一个自反函数,它返回所有扩展给定抽象类的类。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-07-07
    • 1970-01-01
    • 1970-01-01
    • 2013-09-17
    • 2019-02-17
    • 2017-05-04
    相关资源
    最近更新 更多