如何使用标准MVC核心依赖注入解决未注册类型

本文关键字:解决 注册 类型 注入 依赖 何使用 标准 MVC 核心 | 更新日期: 2023-09-27 17:54:42

是否有办法让IServiceProvider.GetService<T>返回一个实例,即使T没有显式地与容器注册?

如果我知道T有依赖关系,我希望它们基于它们的注册被注入,而不必注册T本身。

我相信Ninject会智能地找出最合适的构造函数,或者在没有找到合适的构造函数时回退到无参数构造函数。如果可能的话,我想使用标准MVC Core DI框架复制这种行为。

如何使用标准MVC核心依赖注入解决未注册类型

要保持最新:有一种方法可以实现您正在寻找的东西。

.NET Core的标准依赖注入器

在我的示例中,我为showcase创建了一个新的IServiceCollection实例:
var services = new ServiceCollection();
var serviceProvider = services.BuildServiceProvider();
var unregisteredClassInstance = 
    ActivatorUtilities.CreateInstance<UnregisteredClass>(serviceProvider);

这给了你一个UnregisteredClass类型的实例,它之前没有注册过,但是它接受了之前在ServiceCollection上注册过的构造函数参数。

第三方依赖注入器

SimpleInjector可以通过container.GetInstance<UnregisteredClass>(); .

虽然默认情况下没有提供解析未注册的具体类型的方法,但您可以添加一个扩展函数来提供此功能。需要注意的是,这可能被滥用为服务定位器,这是一种反模式。根据你的使用情况,你可以利用这一点,而不用把它变成一个服务定位器。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace Microsoft.Extensions.DependencyInjection
{
    public static class ServiceProviderExtensions
    {
        public static TService AsSelf<TService>(this IServiceProvider serviceProvider)
        {
            return (TService)AsSelf(serviceProvider, typeof(TService));
        }
        public static object AsSelf(this IServiceProvider serviceProvider, Type serviceType)
        {
            var constructors = serviceType.GetConstructors(BindingFlags.Public | BindingFlags.Instance)
                .Select(o => o.GetParameters())
                .ToArray()
                .OrderByDescending(o => o.Length)
                .ToArray();
            if (!constructors.Any())
            {
                return null;
            }
            object[] arguments = ResolveParameters(serviceProvider, constructors);
            if (arguments == null)
            {
                return null;
            }
            return Activator.CreateInstance(serviceType, arguments);
        }
        private static object[] ResolveParameters(IServiceProvider resolver, ParameterInfo[][] constructors)
        {
            foreach (ParameterInfo[] constructor in constructors)
            {
                bool hasNull = false;
                object[] values = new object[constructor.Length];
                for (int i = 0; i < constructor.Length; i++)
                {
                    var value = resolver.GetService(constructor[i].ParameterType);
                    values[i] = value;
                    if (value == null)
                    {
                        hasNull = true;
                        break;
                    }
                }
                if (!hasNull)
                {
                    // found a constructor we can create.
                    return values;
                }
            }
            return null;
        }
    }
}
public static T? BuildNoneRegisteredType<T>(this IServiceProvider serviceProvider)
{
    var constructor = typeof(T).GetConstructors().First();
    var parameters = constructor.GetParameters();
    if (parameters is null || parameters.Length < 1)
        return Activator.CreateInstance<T>(); 
    var parametersToPas = new object?[parameters.Length];
    for (int i = 0; i < parameters.Length; i++)
    {
        var a = serviceProvider.GetService(parameters[i].ParameterType);
        parametersToPas[i] = a;
    }
    var obj = Activator.CreateInstance(typeof(T), parametersToPas);
    return obj is null ? default : (T)obj;
}

当我有多个数据库并且我不想在解析它们时使用键时,我使用这个。如果我需要mongoDb类型的新数据库,我将这样做:

var myMongoDb = webApp.ServiceProvider.BuildNoneRegisteredType<MongoDb>()