如何使用标准 MVC Core 依赖注入来解析未注册的类型

Posted

技术标签:

【中文标题】如何使用标准 MVC Core 依赖注入来解析未注册的类型【英文标题】:How to resolve un-registered type using standard MVC Core dependency injection 【发布时间】:2016-07-04 10:52:42 【问题描述】:

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

如果我知道 T 具有依赖关系,我希望根据它们的注册来注入它们,而不必注册 T 本身。

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

【问题讨论】:

@Rafal docs.asp.net/en/latest/fundamentals/dependency-injection.html 为了清楚起见,我已将问题更新为 MVC6 【参考方案1】:

保持最新状态:有一种方法可以准确地实现您正在寻找的东西。

.NET Core 的标准依赖注入器

在我的示例中,我为展示创建了一个新的 IServiceCollection 实例:

var services = new ServiceCollection();
var serviceProvider = services.BuildServiceProvider();
var unregisteredClassInstance = 
    ActivatorUtilities.CreateInstance<UnregisteredClass>(serviceProvider);

这为您提供了一个之前未注册的 UnregisteredClass 类型的实例,但采用了之前在 ServiceCollection 上注册的构造函数参数。

第三方依赖注入器

SimpleInjector 可以轻松做到这一点 container.GetInstance&lt;UnregisteredClass&gt;();.

【讨论】:

我喜欢他们拿走了这个众所周知的 Unity“功能”,用另一个 hack 来代替它。 即便如此,它还是不如其他第三方 DI 实用程序强大。 .GetInstance&lt;T&gt;(..).GetServiceOrCreateInstance&lt;T&gt;(..) 只有在它有一个无参数构造函数或者它的所有依赖项都已注册时才能工作。 Ninject 之类的工具在依赖关系树的每个级别都利用“获取服务或创建默认实例”,这意味着它可以创建依赖于其他未注册服务的未注册服务——在 MS DI 中并非如此。【参考方案2】:

虽然默认情况下没有提供解析未注册的具体类型的方法,但您可以添加一个扩展函数来提供该功能。重要的是要注意,这可以作为反模式的服务定位器被滥用。根据您的使用情况,可以在不将其转换为服务定位器的情况下利用这一点。

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;
        
    

【讨论】:

服务位置不是反模式。当您使用它来不根据当前请求定位可变服务而是将其用作桥梁时,这是一种反模式,无法根据当前请求进行动态解析。 Service Locator 的问题是它隐藏了一个类的依赖关系,导致运行时错误而不是编译时错误,并且使代码更难维护,因为当你变得不清楚时将引入一个突破性的变化。 (blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern)。 blog.ploeh.dk/2014/05/15/service-locator-violates-solid 随机调用 services.Get() 是一种反模式。基于可变消息数据在运行时动态解析服务是实现流程机械化并具有真正可重用性的常见模式。另外,也许您应该在链接我的帖子2012-01-31 13:54 UTC 之前检查一下,就好像我不知道一样。 6 年后,我只是比以往任何时候都更加坚定,服务地点有特定的目的。它需要非常小心地部署。

以上是关于如何使用标准 MVC Core 依赖注入来解析未注册的类型的主要内容,如果未能解决你的问题,请参考以下文章

ASP.Net Core MVC 依赖注入不起作用

如何在 ASP.NET Core MVC 中使用依赖注入设计存储库模式?

ASP.NET Core Web 应用程序系列- 在ASP.NET Core中使用Autofac替换自带DI进行批量依赖注入(MVC当中应用)

ASP.NET Core Web 应用程序系列- 在ASP.NET Core中使用Autofac替换自带DI进行构造函数和属性的批量依赖注入(MVC当中应用)

ASP.NET Core Web 应用程序系列- 在ASP.NET Core中使用Autofac替换自带DI进行构造函数和属性的批量依赖注入(MVC当中应用)

ASP.NET Core Web 应用程序系列- 使用ASP.NET Core内置的IoC容器DI进行批量依赖注入(MVC当中应用)