使用 DryIoc 替换 Abp 的 DI 框架

1、背景

你说我 Castle Windsor 库用得好好的,为啥要大费周章的替换成 DryIoc 库呢?那就是性能,DryIoc 是一款优秀并且轻量级的 DI 框架,整个项目代码就两个文件,加起来代码 1 万行左右(PS: 大部分都是注释)。git

在各个 Ioc 容器的 性能评测 当中,DryIoc 以其优异的性能成为我选择使用他的缘由。Abp 使用的 Castle Windsor 在解析复杂对象的时候,速度很是慢,而替换为 DryIoc 以后速度能够提高 150% 以上。github

【注意】数据库

本文仅对 .NET Core 相关的库进行更改并测试,.NET Framework 相关的库并无进行修改测试。数组

2、准备

你须要准备以下原料:session

  1. Abp 源码 一份。
  2. 测试用的项目一份。
  3. Visual Studio 2017 或者 Rider 一份。
  4. .NET 程序猿一枚。

3、分析

首先,Abp 框架的大部分动做基本上都是经过 IIocManager 这个接口对象进行实现的,它抽象了一层做为一个 DI 框架的操做类。它的默认实现是使用的 Castle Windsor 来进行组件的注入与解析,因此咱们只须要将其改成使用 DryIoc 的容器其进行操做便可。app

其次,在 Abp 框架的不少地方都有用到 Castle Windsor 的 IWindsorContainer 对象,但通常用到该方法的地方都是注入或者绑定组件注册事件,这些咱们均可以从新实现的。框架

作完了以上的工做仅仅是表明咱们的 Abp 的全部组件均可以经过 DryIoc 来进行注册和解析,不过要和 ASP.NET Core 集成的话,还须要 IServiceProvider 的适配器,针对于适配器 DryIoc 也给咱们提供了,拿来用便可。ide

因此,咱们基本肯定了须要变动的项目主要是 Abp 这个核心库,还有 Abp.AspNetCore 这个子模块。除了前面两个比较重要的模块以外,还有 Abp.EntityFrameworkCore 相关的库也须要变动,这是由于他们内部都直接使用到了 IWindsorContainer 对象对容器进行操做的。函数

4、开撸

4.1 Abp 库改造

Abp 自己库里面须要改动的地方基本集中在 Dependency 文件夹里面,这个文件夹咱们以前有讲过,基本全部依赖注入相关的类型与接口都存放在这里面的。源码分析

除了依赖注入相关的类型须要更改之外,咱们还须要更改各个拦截器注入的地方。由于在以前 Abp 若是须要为某个类型注入拦截器的话,是使用到了 IWindsorContainer 接口所提供的组件注入事件来进行拦截器注入的。

首先咱们针对于 Abp 库添加 DryIoc 库的 NuGet 包引用,这里我是安装的 3.1.0-preview-06 版本。

4.1.1 IocManger 改造

首先看一下 IIocManager 接口,该接口定义以下:

/// <summary>
/// This interface is used to directly perform dependency injection tasks.
/// </summary>
public interface IIocManager : IIocRegistrar, IIocResolver, IDisposable
{
    /// <summary>
    /// Reference to the Castle Windsor Container.
    /// </summary>
    IWindsorContainer IocContainer { get; }

    /// <summary>
    /// Checks whether given type is registered before.
    /// </summary>
    /// <param name="type">Type to check</param>
    new bool IsRegistered(Type type);

    /// <summary>
    /// Checks whether given type is registered before.
    /// </summary>
    /// <typeparam name="T">Type to check</typeparam>
    new bool IsRegistered<T>();
}

能够看到他定义了一个 IWindsorContainer 的属性,咱们将其改成 IContainer 。基本上作了这一步以后,在 Abp 的其余项目会出现一堆错误提示,先不慌,一步一步来。

接着咱们转到 IIocManager 的实现类 IocManager ,同样的更改 IocContainer 的类型为 IContainer 以后,咱们继续来到其构造函数,能够看到有以下代码:

public IocManager()
{
    IocContainer = CreateContainer();
    _conventionalRegistrars = new List<IConventionalDependencyRegistrar>();

    //Register self!
    IocContainer.Register(
        Component
        .For<IocManager, IIocManager, IIocRegistrar, IIocResolver>()
        .Instance(this)
    );
}

由于咱们的 IocContainer 跟着变动了,这里也不能使用 CreateContainer() 方法来建立 DryIoc 的容器。其次,在下面注册本身的时候,也是使用到了 IWindsorContainer 的注册方法,同样的须要进行更改。变动好的构造函数以下:

public IocManager()
{
    // 这里经过 Rules 启用了瞬态对象跟踪,默认是不启动的。
    IocContainer = new Container(Rules.Default.WithTrackingDisposableTransients());
    _conventionalRegistrars = new List<IConventionalDependencyRegistrar>();

    // 注册自身
    IocContainer.UseInstance(typeof(IocManager),this);
    IocContainer.UseInstance(typeof(IIocManager),this);
    IocContainer.UseInstance(typeof(IIocRegistrar),this);
    IocContainer.UseInstance(typeof(IIocResolver),this);
}

接着就须要继续看一下报错的方法,另外一个须要改的则是注册方法的一个辅助私有方法 ApplyLifestyle,该方法主要做用就是将 Abp 定义的生命周期转换为具体 Ioc 容器的生命周期常量。并且该方法原来是返回的一个 ComponentRegistration<T> 对象,这个对象是 Castle Windsor 的一个专属类,因此须要改造一下,变动以后以下:

private static IReuse ApplyLifestyle(DependencyLifeStyle lifeStyle)
{
    switch (lifeStyle)
    {
        case DependencyLifeStyle.Transient:
            return Reuse.Transient;;
        case DependencyLifeStyle.Singleton:
            return Reuse.Singleton;
        default:
            return Reuse.Transient;
    }
}

作了这个改动以后,剩下的就是须要针对注册与解析方法进行一些改动了,由于 IocManger 提供的注册与解析方法也是调用的具体 Ioc 容器所提供的方法,而 IWindsorContainer 提供的,DryIoc 的 IContainer 基本上也都有提供 ,只是个别特殊的方法有一些不一样而已。

下面是改造完成的部分注册与解析接口(详细的能够查看 Github 代码):

public void Register(Type type, DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton)
{
    IocContainer.Register(type,ApplyLifestyle(lifeStyle));
}

// ... 其余接口
public void Register(Type type, Type impl, DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton)
{
    if (type == impl)
    {
        // 这里经过 made 参数指定了解析对象时优先解析带有参数的构造函数
        IocContainer.Register(type,impl,ApplyLifestyle(lifeStyle),
            made: Made.Of(FactoryMethod.ConstructorWithResolvableArguments));
        RegisterTypeEventHandler?.Invoke(this,type,impl);
    }
    else
    {
        IocContainer.RegisterMany(new[]
            {
                type,
                impl
            },
            impl,
            made: Made.Of(FactoryMethod.ConstructorWithResolvableArguments),
            reuse: ApplyLifestyle(lifeStyle));
        
        RegisterTypeEventHandler?.Invoke(this,type,impl);
        RegisterTypeEventHandler?.Invoke(this,impl,impl);
    }
}

// ... 其余接口

这里须要注意一点的是带参数的解析方法 public T Resolve<T>(object argumentsAsAnonymousType) ,DryIoc 与 Castle Windsor 不一样的是,它可以接收的只能是参数数组,而不能接收一个参数集合的匿名对象。因此咱们须要将入参改成 object[] ,固然也由于变动了方法签名,因此咱们须要更改 ScopedIocResolverIIocResolverIocResolverExtensions 定义里面带参数的解析方法签名。

public T Resolve<T>(object[] argumentsAsAnonymousType)
{
    return IocContainer.Resolve<T>(argumentsAsAnonymousType);
}

其次,还有一个 public T[] ResolveAll<T>() 内部调用了 IocContainer.ResolveAll 方法,而 DryIoc 是没有提供这个方法的,可是有一个 ResolveMany() 方法是同样的做用。下面是进行更改以后的 ResolveAll() 方法的全部重载:

///<inheritdoc/>
public T[] ResolveAll<T>()
{
    return IocContainer.ResolveMany<T>().ToArray();
}

///<inheritdoc/>
public T[] ResolveAll<T>(object[] argumentsAsAnonymousType)
{
    return IocContainer.ResolveMany<T>(args:argumentsAsAnonymousType).ToArray();
}

///<inheritdoc/>
public object[] ResolveAll(Type type)
{
    return IocContainer.ResolveMany(type).ToArray();
}

///<inheritdoc/>
public object[] ResolveAll(Type type, object[] argumentsAsAnonymousType)
{
    return IocContainer.ResolveMany(type, args:argumentsAsAnonymousType).ToArray();
}

除了解析方法以外,还有对象释放的方法 Release,因为 DryIoc 没有提供释放方法,因此这里只能显式地调用对象的 Dispose() 方法来进行释放。

public void Release(object obj)
{
    if(obj is IDisposable disposeObj)
    {
        disposeObj.Dispose();
    }
}

作了以上变动以后,还有一个地方在提示错误:

public void RegisterAssemblyByConvention(Assembly assembly, ConventionalRegistrationConfig config)
{
    var context = new ConventionalRegistrationContext(assembly, this, config);

    foreach (var registerer in _conventionalRegistrars)
    {
        registerer.RegisterAssembly(context);
    }

    if (config.InstallInstallers)
    {
         // 这里仍然使用了 IWindsorContainr 的方法
        IocContainer.Install(FromAssembly.Instance(assembly));
    }
}

看过博主以前更新的 Abp 源码分析的同窗应该知道,这个 Install() 的做用其实很简单,就是直接遍历指定程序集的类型,查找是否有实现了 IWindsorInstaller 接口的对象,若是有则调用其 Install() 方法。而在其 Install() 方法里面,通常都是经过传入的 IIocContainer 或者是 IIocManager 对象来进行组件注册的功能。

在这里,咱们能够针对 IocManager 写两个扩展方法 Intsall() 和一个 IDryIocInstaller 接口用于实现类似的功能。

namespace Abp.Dependency
{
    public interface IDryIocInstaller
    {
        void Install(IIocManager iocManager);
    }
}

扩展方法:

using System;
using System.Linq;
using System.Reflection;

namespace Abp.Dependency
{
    public static class IocManagerExtensions
    {
        public static void Install(this IIocManager iocManager,IDryIocInstaller installer)
        {
            installer.Install(iocManager);
        }

        public static void Install(this IIocManager iocManager, Assembly assembly)
        {
            // 得到指定程序集内部全部的 Installer 类型
            var installers = assembly.GetTypes().Where(type => type.GetInterfaces().Any(@interface => @interface == typeof(IDryIocInstaller)));

            // 遍历类型并经过 Activator 进行构造并调用
            foreach (var installer in installers)
            {
                (Activator.CreateInstance(installer) as IDryIocInstaller)?.Install(iocManager);
            }
        }
    }
}

如今咱们回到最开始报错的地方,将其 Install() 方法改成调用咱们新的扩展方法。

public void RegisterAssemblyByConvention(Assembly assembly, ConventionalRegistrationConfig config)
{
    var context = new ConventionalRegistrationContext(assembly, this, config);

    foreach (var registerer in _conventionalRegistrars)
    {
        registerer.RegisterAssembly(context);
    }

    if (config.InstallInstallers)
    {
        // 调用以前编写的扩展方法
        this.Install(assembly);
    }
}

4.1.2 依赖注入辅助接口改造

Abp 库自己提供了两个接口 (ITransientDependencyISingletonDependency ) 来帮助用户快速地注入某个对象,而后经过注册规约结合 IocManager 提供的 AddConventionalRegistrar() 方法和 RegisterAssemblyByConvention() 方法可以快速地将某个程序集内部符合规则的类型进行注入。(PS: 这里其实流程很像以前 Installer 的作法)

在使用 Castle Windsor 的时候,Abp 自己并不须要作太多的工做,就能够实现上述的功能。而 DryIoc 自己是没有提供这些比较高级的特性的,但原理其实并不复杂, 就是扫描整个程序集的全部类型,而后挨个进行判断便可。

在原来的 BasicConventionalRegistrar 类型内部,对实现了 ITransientDependencyISingletonDependencyIInterceptor 接口的类型进行了自动注册。因此咱们就有了如下的实现代码:

using System;
using System.Linq;
using System.Reflection;
using Abp.Extensions;
using Castle.DynamicProxy;

namespace Abp.Dependency
{
    public class AssemblyType
    {
        public Type ServiceType { get; set; }

        public Type ImplType { get; set; }
    }
    
    /// <summary>
    /// 本类用于注册实现了 <see cref="ITransientDependency"/> 和 <see cref="ISingletonDependency"/> 接口的类型。
    /// </summary>
    public class BasicConventionalRegistrar : IConventionalDependencyRegistrar
    {
        public void RegisterAssembly(IConventionalRegistrationContext context)
        {
            // 瞬时对象注册
            var waitRegisterTransient = GetTypes<ITransientDependency>(context.Assembly).ToList();

            foreach (var transientType in waitRegisterTransient)
            {
                context.IocManager.RegisterIfNot(transientType.ServiceType,transientType.ImplType,DependencyLifeStyle.Transient);
            }
            
            // 单例对象注册
            var waitRegisterSingleton = GetTypes<ISingletonDependency>(context.Assembly).ToList();

            foreach (var singletonType in waitRegisterSingleton)
            {
                context.IocManager.RegisterIfNot(singletonType.ServiceType,singletonType.ImplType,DependencyLifeStyle.Singleton);
            }
            
            // Castle.DynamicProxy 拦截器注册
            var waitRegisterInterceptor = GetTypes<IInterceptor>(context.Assembly).ToList();

            foreach (var interceptorType in waitRegisterInterceptor)
            {
                context.IocManager.RegisterIfNot(interceptorType.ServiceType,interceptorType.ImplType,DependencyLifeStyle.Transient);
            }
        }

        private ParallelQuery<AssemblyType> GetTypes<TInterface>(Assembly assembly)
        {
            Type GetServiceType(Type type)
            {
                var interfaces = type.GetInterfaces().Where(i => i != typeof(TInterface));

                // 优先匹配去除 I 以后的接口
                var defaultInterface = interfaces.FirstOrDefault(i => type.Name.Equals(i.Name.RemovePreFix("I")));
                if (defaultInterface != null) return defaultInterface;
                if (interfaces.FirstOrDefault() != null) return interfaces.FirstOrDefault();
                return type;
            }

            return assembly.GetTypes()
                .AsParallel()
                .Where(type => typeof(TInterface).IsAssignableFrom(type))
                .Where(type => type.GetInterfaces().Any() && !type.IsInterface)
                .Where(type => !type.IsGenericTypeDefinition)
                .Where(type => !type.IsAbstract)
                .Select(type => new AssemblyType
                {
                    ServiceType = GetServiceType(type),
                    ImplType = type
                });
        }
    }
}

在咱们实现的新的注册规约当中能够看到,其实最核心的代码在于 GetTypes() 方法内部,在其内部进行了比较复杂的判断逻辑,其他的瞬时对象与单例对象的注入,都是直接调用的 IIocManager 接口所提供的注册方法。

4.1.3 拦截器绑定

由于没有使用 Castle Windsor ,那么咱们拦截器如何使用?又如何与类型进行绑定的呢?

在 DryIoc 官方文档已经说明,DryIoc 自己的拦截功能也是经过 Castle Dynamic Proxy 来实现的,因此咱们只须要编写一个辅助的静态扩展类便可。

using System;
using System.Linq;
using Castle.DynamicProxy;
using DryIoc;
using ImTools;

public static class DryIocInterception
{
    static readonly DefaultProxyBuilder ProxyBuilder = new DefaultProxyBuilder();

    public static void Intercept(this IRegistrator registrator,Type serviceType,Type interceptorType,Type implType, object serviceKey = null)
    {
        // 判断传入的类型是接口仍是类型,以便创建代理类
        Type proxyType;
        if (serviceType.IsInterface())
            proxyType = ProxyBuilder.CreateInterfaceProxyTypeWithTargetInterface(
                serviceType, ArrayTools.Empty<Type>(), ProxyGenerationOptions.Default);
        else if (serviceType.IsClass())
            proxyType = ProxyBuilder.CreateClassProxyTypeWithTarget(
                serviceType, ArrayTools.Empty<Type>(), ProxyGenerationOptions.Default);
        else
            throw new ArgumentException(
                $"Intercepted service type {serviceType} is not a supported, cause it is nor a class nor an interface");

        // 建立 DryIoc 装饰器
        var decoratorSetup = serviceKey == null
            ? Setup.DecoratorWith(useDecorateeReuse: true)
            : Setup.DecoratorWith(r => serviceKey.Equals(r.ServiceKey), useDecorateeReuse: true);

        // 替换注册原来接口的解析,解析到新的代理类
        registrator.Register(serviceType, proxyType,
            made: Made.Of((Type type) => type.GetConstructors().SingleOrDefault(c => c.GetParameters().Length != 0), 
                Parameters.Of.Type<IInterceptor[]>(interceptorType.MakeArrayType()),
                // 必定要加上这个,否则属性注入没法使用
                PropertiesAndFields.Auto),
            setup: decoratorSetup);
    }
    
    public static void Intercept<TService,TImplType, TInterceptor>(this IRegistrator registrator, object serviceKey = null) 
        where TInterceptor : class, IInterceptor
    {
        Intercept(registrator,typeof(TService),typeof(TInterceptor),typeof(TImplType),serviceKey);
    }
}

这个扩展类的用法,在后面就有体现。

4.1.4 拦截器注册器绑定事件

最开始 Abp 拦截器是在何时与具体类型绑定的呢?其实就是在 Castle Windsor 注入组件的时候,各个拦截器注册器都会监听这个组件注入事件。当事件被触发的时候,Abp 各个拦截器注册器都会执行一系列的判断来确保当前类型应该绑定哪个拦截器。

Abp 自带的拦截器一共有 5 种:工做单元拦截器、参数验证拦截器、受权拦截器、审计日志拦截器、实体历史拦截器。这五种拦截器都是在 AbpBootstrapper 执行建立方法的时候会被调用,调用的时候会监听组件注册事件。

如今的问题是,咱们已经没有使用 Castle Windsor 也就没有办法使用 IWindsorContainer 来监听组件注册事件。而 DryIoc 自己也是没有提供这种注入事件的,因此这里咱们就只有抽象到 IocManager 类型当中,当 IocManager 的几个注册方法被调用的时候,显式触发一个事件通知这些拦截器注册器对象。

首先咱们来到 IIocManager 接口,为其添加一个公开的委托属性,该委托的定义也在下面给出来了。

委托定义:

using System;

namespace Abp.Dependency
{
    public delegate void RegisterTypeEventHandler(IIocManager iocManager, Type registerType,Type implementationType);
}

IIocManager 接口处新增的属性:

using System;
using DryIoc;

namespace Abp.Dependency
{
    /// <summary>
    /// 依赖注入容器管理器,
    /// 本接口用于执行注入操做
    /// </summary>
    public interface IIocManager : IIocRegistrar, IIocResolver, IDisposable
    {
        IContainer IocContainer { get; }

        new bool IsRegistered(Type type);

        new bool IsRegistered<T>();

        event RegisterTypeEventHandler RegisterTypeEventHandler;
    }
}

以后呢,咱们在 IocManagerRegister() 注册方法内部都显式地触发这个事件。

public void Register(Type type, DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton)
{
    IocContainer.Register(type,ApplyLifestyle(lifeStyle),
        made: Made.Of(FactoryMethod.ConstructorWithResolvableArguments));
    RegisterTypeEventHandler?.Invoke(this,type,type);
}

就如同这样,实现的效果也是每当有组件注册的时候,都会触发该事件。而各个注册器内部的 Initialize() 方法都传入了一个 IIocManager 对象,因此咱们只须要将原有的监听事件改成绑定咱们本身定义的事件便可。

下面以工做单元的拦截器注册器为例:

using System.Linq;
using System.Reflection;
using Abp.Dependency;
using Castle.Core;
using Castle.MicroKernel;

namespace Abp.Domain.Uow
{
    /// <summary>
    /// This class is used to register interceptor for needed classes for Unit Of Work mechanism.
    /// </summary>
    internal static class UnitOfWorkRegistrar
    {
        /// <summary>
        /// Initializes the registerer.
        /// </summary>
        /// <param name="iocManager">IOC manager</param>
        public static void Initialize(IIocManager iocManager)
        {
            iocManager.RegisterTypeEventHandler += (manager, type, implementationType) =>
            {
                var implType = implementationType.GetTypeInfo();

                HandleTypesWithUnitOfWorkAttribute(implType,manager);
                HandleConventionalUnitOfWorkTypes(iocManager, implType);
            };
        }

        private static void HandleTypesWithUnitOfWorkAttribute(TypeInfo implementationType,IIocManager iocManager)
        {
            if (IsUnitOfWorkType(implementationType) || AnyMethodHasUnitOfWork(implementationType))
            {
                // 使用的是上面写的扩展方法
                iocManager.IocContainer.Intercept(implementationType,typeof(UnitOfWorkInterceptor));
            }
        }

        private static void HandleConventionalUnitOfWorkTypes(IIocManager iocManager, TypeInfo implementationType)
        {
            if (!iocManager.IsRegistered<IUnitOfWorkDefaultOptions>())
            {
                return;
            }

            var uowOptions = iocManager.Resolve<IUnitOfWorkDefaultOptions>();

            if (uowOptions.IsConventionalUowClass(implementationType.AsType()))
            {
                // 使用的是上面写的扩展方法
                iocManager.IocContainer.Intercept(implementationType,typeof(UnitOfWorkInterceptor));
            }
        }

        private static bool IsUnitOfWorkType(TypeInfo implementationType)
        {
            return UnitOfWorkHelper.HasUnitOfWorkAttribute(implementationType);
        }

        private static bool AnyMethodHasUnitOfWork(TypeInfo implementationType)
        {
            return implementationType
                .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                .Any(UnitOfWorkHelper.HasUnitOfWorkAttribute);
        }
    }
}

按照上面这种步骤,完成剩余拦截器注册器的更改。

4.1.5 收尾工做

若是上述操做都已经完成了的话,那么基本上只剩下 AbpBootstrapper 类型与 AbpKernelModule 几处细小的错误了。

首先咱们看一下 AbpBootstrapper 还提示哪些错误,而后咱们进行更改。

// 第一处
public virtual void Initialize()
{
    ResolveLogger();

    try
    {
        RegisterBootstrapper();
        
        // IocManager.IocContainer.Install(new AbpCoreInstaller());
        // 此处使用的仍然是 IWindsorContainer 的 Install 方法,改成最新的
        IocManager.Install(new AbpCoreInstaller());

        IocManager.Resolve<AbpPlugInManager>().PlugInSources.AddRange(PlugInSources);
        IocManager.Resolve<AbpStartupConfiguration>().Initialize();

        _moduleManager = IocManager.Resolve<AbpModuleManager>();
        _moduleManager.Initialize(StartupModule);
        _moduleManager.StartModules();
    }
    catch (Exception ex)
    {
        _logger.Fatal(ex.ToString(), ex);
        throw;
    }
}

上面仍然报错,咱们继续来到 AbpCoreInstaller 将其接口由 IWindsorInstaller 改成 IDryIocInstaller 并从新实现接口的方法。

using Abp.Application.Features;
using Abp.Auditing;
using Abp.BackgroundJobs;
using Abp.Configuration.Startup;
using Abp.Domain.Uow;
using Abp.EntityHistory;
using Abp.Localization;
using Abp.Modules;
using Abp.Notifications;
using Abp.PlugIns;
using Abp.Reflection;
using Abp.Resources.Embedded;
using Abp.Runtime.Caching.Configuration;
using DryIoc;

namespace Abp.Dependency.Installers
{
    /// <summary>
    /// ABP 框架核心类安装器
    /// 本类用于注册 ABP 框架当中核心组件
    /// </summary>
    internal class AbpCoreInstaller : IDryIocInstaller
    {
        public void Install(IIocManager iocManager)
        {
            iocManager.IocContainer.RegisterMany(new[] {typeof(IUnitOfWorkDefaultOptions), typeof(UnitOfWorkDefaultOptions)}, typeof(UnitOfWorkDefaultOptions), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(INavigationConfiguration), typeof(NavigationConfiguration)}, typeof(NavigationConfiguration), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(ILocalizationConfiguration), typeof(LocalizationConfiguration)}, typeof(LocalizationConfiguration), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(IAuthorizationConfiguration), typeof(AuthorizationConfiguration)}, typeof(AuthorizationConfiguration), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(IValidationConfiguration), typeof(ValidationConfiguration)}, typeof(ValidationConfiguration), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(IFeatureConfiguration), typeof(FeatureConfiguration)}, typeof(FeatureConfiguration), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(ISettingsConfiguration), typeof(SettingsConfiguration)}, typeof(SettingsConfiguration), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(IModuleConfigurations), typeof(ModuleConfigurations)}, typeof(ModuleConfigurations), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(IEventBusConfiguration), typeof(EventBusConfiguration)}, typeof(EventBusConfiguration), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(IMultiTenancyConfig), typeof(MultiTenancyConfig)}, typeof(MultiTenancyConfig), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(ICachingConfiguration), typeof(CachingConfiguration)}, typeof(CachingConfiguration), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(IAuditingConfiguration), typeof(AuditingConfiguration)}, typeof(AuditingConfiguration), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(IBackgroundJobConfiguration), typeof(BackgroundJobConfiguration)}, typeof(BackgroundJobConfiguration), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(INotificationConfiguration), typeof(NotificationConfiguration)}, typeof(NotificationConfiguration), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(IEmbeddedResourcesConfiguration), typeof(EmbeddedResourcesConfiguration)}, typeof(EmbeddedResourcesConfiguration), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(IAbpStartupConfiguration), typeof(AbpStartupConfiguration)}, typeof(AbpStartupConfiguration), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(IEntityHistoryConfiguration), typeof(EntityHistoryConfiguration)}, typeof(EntityHistoryConfiguration), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(ITypeFinder), typeof(TypeFinder)}, typeof(TypeFinder), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(IAbpPlugInManager), typeof(AbpPlugInManager)}, typeof(AbpPlugInManager), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(IAbpModuleManager), typeof(AbpModuleManager)}, typeof(AbpModuleManager), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(IAssemblyFinder), typeof(AbpAssemblyFinder)}, typeof(AbpAssemblyFinder), Reuse.Singleton);
            iocManager.IocContainer.RegisterMany(new[] {typeof(ILocalizationManager), typeof(LocalizationManager)}, typeof(LocalizationManager), Reuse.Singleton);
        }
    }
}

AbpBootstrapper 类型还有一处问题同样是使用了 IWindsorContainer 提供的方法,这里改成 DryIoc 提供的方法便可。

private void RegisterBootstrapper()
{
    if (!IocManager.IsRegistered<AbpBootstrapper>())
    {
        //                IocManager.IocContainer.Register(
        //                    Component.For<AbpBootstrapper>().Instance(this)
        //                    );
        IocManager.IocContainer.UseInstance(this);
    }
}

第二个问题则是 AbpKernelModule 当中的报错,其实与上一个类型的错误同样,第一个是调用了以前的 Install 的方法,而且 Intsaller 也不是继承自 IDryIocInstaller,另外一个问题则是使用了 IWindsorContainer 里面的注册方法。

public override void Initialize()
{
    foreach (var replaceAction in ((AbpStartupConfiguration)Configuration).ServiceReplaceActions.Values)
    {
        replaceAction();
    }

    //            IocManager.IocContainer.Install(new EventBusInstaller(IocManager));
    IocManager.Install(new EventBusInstaller(IocManager));

    IocManager.Register(typeof(IOnlineClientManager<>), typeof(OnlineClientManager<>), DependencyLifeStyle.Singleton);

    IocManager.RegisterAssemblyByConvention(typeof(AbpKernelModule).GetAssembly(),
                                            new ConventionalRegistrationConfig
                                            {
                                                InstallInstallers = false
                                            });
}

EventBusInstaller 的变动:

using System.Reflection;
using Abp.Configuration.Startup;
using Abp.Dependency;
using Abp.Events.Bus.Factories;
using Abp.Events.Bus.Handlers;
using Castle.MicroKernel;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
using DryIoc;

namespace Abp.Events.Bus
{
    /// <summary>
    /// Installs event bus system and registers all handlers automatically.
    /// </summary>
    internal class EventBusInstaller : IDryIocInstaller
    {
        private readonly IIocResolver _iocResolver;
        private readonly IEventBusConfiguration _eventBusConfiguration;
        private IEventBus _eventBus;

        public EventBusInstaller(IIocResolver iocResolver)
        {
            _iocResolver = iocResolver;
            _eventBusConfiguration = iocResolver.Resolve<IEventBusConfiguration>();
        }
        
        public void Install(IIocManager iocManager)
        {
            if (_eventBusConfiguration.UseDefaultEventBus)
            {
                iocManager.IocContainer.UseInstance<IEventBus>(EventBus.Default);
            }
            else
            {
                iocManager.IocContainer.Register<IEventBus,EventBus>(Reuse.Singleton);
            }

            _eventBus = iocManager.Resolve<IEventBus>();
            iocManager.RegisterTypeEventHandler += (manager, type, implementationType) =>
            {
                if (!typeof(IEventHandler).GetTypeInfo().IsAssignableFrom(implementationType))
                {
                    return;
                }

                var interfaces = implementationType.GetTypeInfo().GetInterfaces();
                foreach (var @interface in interfaces)
                {
                    if (!typeof(IEventHandler).GetTypeInfo().IsAssignableFrom(@interface))
                    {
                        continue;
                    }

                    var genericArgs = @interface.GetGenericArguments();
                    if (genericArgs.Length == 1)
                    {
                        _eventBus.Register(genericArgs[0], new IocHandlerFactory(_iocResolver, implementationType));
                    }
                }
            };
        }
    }
}

另一处的变动以下:

private void RegisterMissingComponents()
{
    if (!IocManager.IsRegistered<IGuidGenerator>())
    {
        //                IocManager.IocContainer.Register(
        //                    Component
        //                        .For<IGuidGenerator, SequentialGuidGenerator>()
        //                        .Instance(SequentialGuidGenerator.Instance)
        //                );
        IocManager.IocContainer.UseInstance<IGuidGenerator>(SequentialGuidGenerator.Instance);
        IocManager.IocContainer.UseInstance<SequentialGuidGenerator>(SequentialGuidGenerator.Instance);
    }

    IocManager.RegisterIfNot<IUnitOfWork, NullUnitOfWork>(DependencyLifeStyle.Transient);
    IocManager.RegisterIfNot<IAuditingStore, SimpleLogAuditingStore>(DependencyLifeStyle.Singleton);
    IocManager.RegisterIfNot<IPermissionChecker, NullPermissionChecker>(DependencyLifeStyle.Singleton);
    IocManager.RegisterIfNot<IRealTimeNotifier, NullRealTimeNotifier>(DependencyLifeStyle.Singleton);
    IocManager.RegisterIfNot<INotificationStore, NullNotificationStore>(DependencyLifeStyle.Singleton);
    IocManager.RegisterIfNot<IUnitOfWorkFilterExecuter, NullUnitOfWorkFilterExecuter>(DependencyLifeStyle.Singleton);
    IocManager.RegisterIfNot<IClientInfoProvider, NullClientInfoProvider>(DependencyLifeStyle.Singleton);
    IocManager.RegisterIfNot<ITenantStore, NullTenantStore>(DependencyLifeStyle.Singleton);
    IocManager.RegisterIfNot<ITenantResolverCache, NullTenantResolverCache>(DependencyLifeStyle.Singleton);
    IocManager.RegisterIfNot<IEntityHistoryStore, NullEntityHistoryStore>(DependencyLifeStyle.Singleton);

    if (Configuration.BackgroundJobs.IsJobExecutionEnabled)
    {
        IocManager.RegisterIfNot<IBackgroundJobStore, InMemoryBackgroundJobStore>(DependencyLifeStyle.Singleton);
    }
    else
    {
        IocManager.RegisterIfNot<IBackgroundJobStore, NullBackgroundJobStore>(DependencyLifeStyle.Singleton);
    }
}

4.1.6 测试

作完以上变动以后,新建一个控制台程序,引用这个 Abp 库项目,而后键入如下代码进行测试便可。

using System;
using Abp;
using Abp.Modules;
using Abp.Runtime.Session;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            // Abp 框架测试
            using (var bootstarp = AbpBootstrapper.Create<StartupModule>())
            {
                bootstarp.Initialize();
                
                // 解析 IAbpSession 看是否正常地进行了注入
                var session = bootstarp.IocManager.Resolve<IAbpSession>();

                if (session != null && session is ClaimsAbpSession claimsSession)
                {
                    Console.WriteLine("当前 Session 已经成功被注入为 ClaimAbpSession");
                }
            }

            Console.ReadLine();
        }
    }

    [DependsOn(typeof(AbpKernelModule))]
    public class StartupModule : AbpModule
    {
        
    }
}

4.2 EFCore 库与相关库改造

针对 Abp 库进行测试以后,基本上咱们 Abp 如今全部组件都是经过 DryIoc 来进行注册与解析的了。不过仅仅针对 Abp 作这些更改实际上是不够的,除了 Abp 核心库以外,咱们最经常使用的就是数据库操做了。由于在 Abp.EntityFrameworkCore 库 和 Abp.EntityFramework.Common 的内部也有部分代码在以前是直接经过 IWindsorContainer 进行注册与解析操做的,因此咱们也得继续改报错的地方。

4.2.1 仓储类注册

Abp.EntityFramework.Common 库的 EfGenericRepositoryRegistrar 类型内部,有使用到 IWindsorContainer 的组件注册方法,用于注入 IRepository<,> 泛型仓储。下面代码展现的更改后的结果:

private void RegisterForDbContext(
    Type dbContextType, 
    IIocManager iocManager,
    Type repositoryInterface,
    Type repositoryInterfaceWithPrimaryKey,
    Type repositoryImplementation,
    Type repositoryImplementationWithPrimaryKey)
{
    foreach (var entityTypeInfo in _dbContextEntityFinder.GetEntityTypeInfos(dbContextType))
    {
        var primaryKeyType = EntityHelper.GetPrimaryKeyType(entityTypeInfo.EntityType);
        if (primaryKeyType == typeof(int))
        {
            var genericRepositoryType = repositoryInterface.MakeGenericType(entityTypeInfo.EntityType);
            if (!iocManager.IsRegistered(genericRepositoryType))
            {
                var implType = repositoryImplementation.GetGenericArguments().Length == 1
                    ? repositoryImplementation.MakeGenericType(entityTypeInfo.EntityType)
                    : repositoryImplementation.MakeGenericType(entityTypeInfo.DeclaringType,
                        entityTypeInfo.EntityType);

//                        iocManager.IocContainer.Register(
//                            Component
//                                .For(genericRepositoryType)
//                                .ImplementedBy(implType)
//                                .Named(Guid.NewGuid().ToString("N"))
//                                .LifestyleTransient()
//                        );
                iocManager.IocContainer.Register(genericRepositoryType,implType,Reuse.Transient);
            }
        }

        var genericRepositoryTypeWithPrimaryKey = repositoryInterfaceWithPrimaryKey.MakeGenericType(entityTypeInfo.EntityType,primaryKeyType);
        if (!iocManager.IsRegistered(genericRepositoryTypeWithPrimaryKey))
        {
            var implType = repositoryImplementationWithPrimaryKey.GetGenericArguments().Length == 2
                ? repositoryImplementationWithPrimaryKey.MakeGenericType(entityTypeInfo.EntityType, primaryKeyType)
                : repositoryImplementationWithPrimaryKey.MakeGenericType(entityTypeInfo.DeclaringType, entityTypeInfo.EntityType, primaryKeyType);

//                    iocManager.IocContainer.Register(
//                        Component
//                            .For(genericRepositoryTypeWithPrimaryKey)
//                            .ImplementedBy(implType)
//                            .Named(Guid.NewGuid().ToString("N"))
//                            .LifestyleTransient()
//                    );
            iocManager.IocContainer.Register(genericRepositoryTypeWithPrimaryKey,implType,Reuse.Transient);
        }
    }
}

按照以上方法更改以后,Abp.EntityFramework.Common 应该能够正常地进行编译了。

4.2.2 DbContext 配置类更改

AbpEfCoreConfiguration 类型当中,也有使用到 IWindsorContainer 接口的地方,进行以下变动便可:

using System;
using Abp.Dependency;
using Castle.MicroKernel.Registration;
using DryIoc;
using Microsoft.EntityFrameworkCore;

namespace Abp.EntityFrameworkCore.Configuration
{
    public class AbpEfCoreConfiguration : IAbpEfCoreConfiguration
    {
        private readonly IIocManager _iocManager;

        public AbpEfCoreConfiguration(IIocManager iocManager)
        {
            _iocManager = iocManager;
        }

        public void AddDbContext<TDbContext>(Action<AbpDbContextConfiguration<TDbContext>> action) 
            where TDbContext : DbContext
        {
//            _iocManager.IocContainer.Register(
//                Component.For<IAbpDbContextConfigurer<TDbContext>>().Instance(
//                    new AbpDbContextConfigurerAction<TDbContext>(action)
//                ).IsDefault()
//            );
            _iocManager.IocContainer.UseInstance<IAbpDbContextConfigurer<TDbContext>>(new AbpDbContextConfigurerAction<TDbContext>(action));
        }
    }
}

4.2.3 EFCore 库模块变动

该错误在 AbpEntityFrameworkCoreModule 模块的 Initialize() 方法里面,同样的是由于使用了 IWndsorContainer 的注册方法致使的。

public override void Initialize()
{
    IocManager.RegisterAssemblyByConvention(typeof(AbpEntityFrameworkCoreModule).Assembly);

//            IocManager.IocContainer.Register(
//                Component.For(typeof(IDbContextProvider<>))
//                    .ImplementedBy(typeof(UnitOfWorkDbContextProvider<>))
//                    .LifestyleTransient()
//                );
    IocManager.IocContainer.Register(typeof(IDbContextProvider<>),typeof(UnitOfWorkDbContextProvider<>),Reuse.Transient);

    RegisterGenericRepositoriesAndMatchDbContexes();
}

而另外一处错误则是在 RegisterGenericRepositoriesAndMatchDbContexes() 方法内部:

private void RegisterGenericRepositoriesAndMatchDbContexes()
{
    // ... 其余的代码
    using (IScopedIocResolver scope = IocManager.CreateScope())
    {
        foreach (var dbContextType in dbContextTypes)
        {
            Logger.Debug("Registering DbContext: " + dbContextType.AssemblyQualifiedName);

            scope.Resolve<IEfGenericRepositoryRegistrar>().RegisterForDbContext(dbContextType, IocManager, EfCoreAutoRepositoryTypes.Default);

//                    IocManager.IocContainer.Register(
//                        Component.For<ISecondaryOrmRegistrar>()
//                            .Named(Guid.NewGuid().ToString("N"))
//                            .Instance(new EfCoreBasedSecondaryOrmRegistrar(dbContextType, scope.Resolve<IDbContextEntityFinder>()))
//                            .LifestyleTransient()
//                    );
            IocManager.IocContainer.UseInstance<ISecondaryOrmRegistrar>(new EfCoreBasedSecondaryOrmRegistrar(dbContextType,
                scope.Resolve<IDbContextEntityFinder>()));
        }

        scope.Resolve<IDbContextTypeMatcher>().Populate(dbContextTypes);
    }
}

4.2.4 DbContext 解析器变动

这个解析器的主要问题则与前面的不同,这里报错是由于在构造 DbContext 的时候须要传入构造参数。根据咱们以前的改动,如今 Resolve() 方法传入的是一个 object[] 数组,而不是原来的 object 对象,因此这里须要进行一些细微的改动。

using Abp.Dependency;
using Abp.EntityFramework;
using Abp.EntityFrameworkCore.Configuration;
using Microsoft.EntityFrameworkCore;
using System;
using System.Data.Common;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using System.Linq;

namespace Abp.EntityFrameworkCore
{
    public class DefaultDbContextResolver : IDbContextResolver, ITransientDependency
    {
        // ... 其余代码
    
        public TDbContext Resolve<TDbContext>(string connectionString, DbConnection existingConnection)
            where TDbContext : DbContext
        {
        
            // ... 其余代码

            try
            {
                if (isAbstractDbContext)
                {
//                    return (TDbContext) _iocResolver.Resolve(concreteType, new
//                    {
//                        options = CreateOptionsForType(concreteType, connectionString, existingConnection)
//                    });
                    
                    return (TDbContext) _iocResolver.Resolve(concreteType, new object[]
                    {
                        CreateOptionsForType(concreteType, connectionString, existingConnection)
                    });
                }

//                return _iocResolver.Resolve<TDbContext>(new
//                {
//                    options = CreateOptions<TDbContext>(connectionString, existingConnection)
//                });

                return _iocResolver.Resolve<TDbContext>(new object[]
                {
                    CreateOptions<TDbContext>(connectionString, existingConnection)
                });
            }
            catch (Castle.MicroKernel.Resolvers.DependencyResolverException ex)
            {
                // ... 其余代码
            }
            
            // ... 其余代码
        }

        // ... 其余代码
    }
}

至此,针对于 EFCore 相关的库改造就已经成功完成了。

4.3 ASP .NET Core 相关改造

到目前,咱们已经针对 Abp 的核心库和 EF Core 库都进行了一些不算大的改动,如今就只剩 Abp.AspNetCore 库了。由于 .NET Core 本身使用了一套 DI 框架。而咱们在以前的源码分析也有讲到过,经过更改 Startup 类的 ConfigureService() 方法的返回值为 IServiceProvider,就能够将原来内部的 DI 框架替换为其余的 DI 框架。

在原来 Abp.AspNetCore 库的 AbpServiceCollectionExtensions 扩展类当中能够看到如下代码:

public static IServiceProvider AddAbp<TStartupModule>(this IServiceCollection services, [CanBeNull] Action<AbpBootstrapperOptions> optionsAction = null)
    where TStartupModule : AbpModule
{
    var abpBootstrapper = AddAbpBootstrapper<TStartupModule>(services, optionsAction);

    ConfigureAspNetCore(services, abpBootstrapper.IocManager);

    return WindsorRegistrationHelper.CreateServiceProvider(abpBootstrapper.IocManager.IocContainer, services);
}

这里咱们能够看到,Abp 经过 WindsorRegistrationHelper 类建立并返回了一个 IServiceProvider 对象。那么 DryIoc 是否也为咱们提供了这样的扩展方法呢?答案是有的,DryIoc 经过 DryIoc.Microsoft.DependencyInjection 给咱们提供了一个适配器,该适配器能够基于 DryIoc 建立一个 IServiceProvier 来替换掉默认的 DI 框架。

首先咱们为 Abp.AspNetCore 库添加 DryIoc.Microsoft.DependencyInjection 的 NuGet 包,而后编辑上述方法:

public static IServiceProvider AddAbp<TStartupModule>(this IServiceCollection services, [CanBeNull] Action<AbpBootstrapperOptions> optionsAction = null)
    where TStartupModule : AbpModule
{
    var abpBootstrapper = AddAbpBootstrapper<TStartupModule>(services, optionsAction);

    ConfigureAspNetCore(services, abpBootstrapper.IocManager);

    var newContainer = new Container(rules =>
            rules.WithAutoConcreteTypeResolution())
        .WithDependencyInjectionAdapter(services);
    
    abpBootstrapper.IocManager.InitializeInternalContainer(newContainer);
    
    return abpBootstrapper.IocManager.IocContainer.BuildServiceProvider();
}

4.3.1 视图组件与其余组件的自动注册

除了更改上述问题以外,在 Abp.AspNetCore 库还有一个注册器 AbpAspNetCoreConventionalRegistrar,在里面也使用了 IWindsorContainer 接口的注册方法,此处也须要进行更改。

using System.Linq;
using Abp.Dependency;
using Microsoft.AspNetCore.Mvc;

namespace Abp.AspNetCore
{
    public class AbpAspNetCoreConventionalRegistrar : IConventionalDependencyRegistrar
    {
        public void RegisterAssembly(IConventionalRegistrationContext context)
        {
            //ViewComponents
            var types = context.Assembly.GetTypes()
                .AsParallel()
                .Where(type => typeof(ViewComponent).IsAssignableFrom(type))
                .Where(type => !type.IsGenericTypeDefinition)
                .Where(type => !type.IsAbstract)
                .AsSequential();

            foreach (var type in types)
            {
                context.IocManager.Register(type);
            }
        }
    }
}

完成以上操做以后,咱们新建 4 个项目,分别是 AspNetCoreApp(Web 项目)AspNetCoreApp.Core(库项目)AspNetCore.Application(库项目)AspNetCoreApp.EntityFrameworkCore(库项目) ,而且配置好各自的依赖关系。

4.3.2 IServiceProvider 适配器

首先咱们更改 AspNetCoreApp 下面的 ConfigureService() 方法与 Configure() 方法以下:

using System;
using Abp.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;

namespace AspNetCoreApp
{
    public class Startup
    {
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            return services.AddAbp<AspNetCoreAppModule>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseMvc();
            app.UseAbp(op=>op.UseCastleLoggerFactory = false);
        }
    }
}

不出意外的话,会抛出如下异常信息:

上述异常的意思是说没法解析 Microsoft.AspNetCore.Hosting.Internal.WebHostOptions 对象,这说明咱们的 DryIoc 容器并无将 MVC 服务初始化注入的对象获取到。

咱们在 AddAbp<TStartupModule>() 方法内打一个断点,看一下在 ConfigureAspNetCore() 方法内部注入的对象是否放在 IContainer 里面,结果发现并无。

因此以后呢,我通过测试,只有 new 一个新的 Container 对象,而后对其调用 WithDependencyInjectionAdapter() 方法才会正常的获取到注入的 MVC 组件。

效果:

那么就须要将 IocManager 内部的 IocContainer 赋值为这里建立的 newContainer 对象,而 IIocManager 接口所定义的 IocContainer 属性是只读的。因此这里我为 IIocManager 接口新增了一个 InitializeInternalContainer() 方法用于初始化 IocContainer 属性。

public interface IIocManager : IIocRegistrar, IIocResolver, IDisposable
{

    // ... 其余代码
    
    /// <summary>
    /// 类型注册事件
    /// </summary>
    event RegisterTypeEventHandler RegisterTypeEventHandler;

    /// <summary>
    /// 初始化 IocManager 内部的容器
    /// </summary>
    void InitializeInternalContainer(IContainer dryIocContainer);
}

IocManager 须要实现该方法,而且将其构造器内的相关注册方法移动到 InitializeInternalContainer() 内部。

public class IocManager : IIocManager
{
    // ... 其余代码

    public IocManager()
    {
        _conventionalRegistrars = new List<IConventionalDependencyRegistrar>();
    }
    
    public void InitializeInternalContainer(IContainer dryIocContainer)
    {
        IocContainer = dryIocContainer;
        
        //Register self!
        IocContainer.UseInstance(typeof(IocManager),this);
        IocContainer.UseInstance(typeof(IIocManager),this);
        IocContainer.UseInstance(typeof(IIocRegistrar),this);
        IocContainer.UseInstance(typeof(IIocResolver),this);
    }
    
    // ... 其余代码
}

以后再回到最开始的地方,咱们最终 AddAbp<TStartupModule>() 方法的内部实现是下面这个样子的:

public static IServiceProvider AddAbp<TStartupModule>(this IServiceCollection services, [CanBeNull] Action<AbpBootstrapperOptions> optionsAction = null)
    where TStartupModule : AbpModule
{
    var abpBootstrapper = AddAbpBootstrapper<TStartupModule>(services, optionsAction);

    ConfigureAspNetCore(services, abpBootstrapper.IocManager);

    var newContainer = new Container().WithDependencyInjectionAdapter(services);
    abpBootstrapper.IocManager.InitializeInternalContainer(newContainer);
    
    return abpBootstrapper.IocManager.IocContainer.BuildServiceProvider();
}

运行 AspNetCoreApp 项目,咱们能够看到正常运行了。

5、存在的问题

5.1 ApplicationService 属性注入失效

在示例项目当中,我在 AspNetCoreApp.Application 库当中创建了一个 TestApplicationService 服务,该服务用有一个 GetJson() 方法。

在其内部,我调用了父类提供的 AbpSession 属性,按照正常的状况下,该属性的实现应该是 ClaimsAbpSession 类型,不过经过测试以后我获得了如下结果:

能够看到,它填充的是默认的空实现,形成这个问题的缘由是,DryIoc 自己在注册对象的时候,须要显式提供属性注入的选项,不然默认是不启用属性注入的。

鉴于此,咱们为 IIocRegistrarIocManager 内所提供的 Register() 方法增长一个 isAutoInjectProperty 字段,用于判断是否在注册的使用启用属性注入。

public interface IIocRegistrar
{
    /// <summary>
    /// Registers a type as self registration.
    /// </summary>
    /// <typeparam name="T">Type of the class</typeparam>
    /// <param name="lifeStyle">Lifestyle of the objects of this type</param>
    void Register<T>(DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton,bool isAutoInjectProperty = false)
        where T : class;

    /// <summary>
    /// Registers a type as self registration.
    /// </summary>
    /// <param name="type">Type of the class</param>
    /// <param name="lifeStyle">Lifestyle of the objects of this type</param>
    void Register(Type type, DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton,bool isAutoInjectProperty = false);

    /// <summary>
    /// Registers a type with it's implementation.
    /// </summary>
    /// <typeparam name="TType">Registering type</typeparam>
    /// <typeparam name="TImpl">The type that implements <see cref="TType"/></typeparam>
    /// <param name="lifeStyle">Lifestyle of the objects of this type</param>
    void Register<TType, TImpl>(DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton,bool isAutoInjectProperty = false)
        where TType : class
        where TImpl : class, TType;

    /// <summary>
    /// Registers a type with it's implementation.
    /// </summary>
    /// <param name="type">Type of the class</param>
    /// <param name="impl">The type that implements <paramref name="type"/></param>
    /// <param name="lifeStyle">Lifestyle of the objects of this type</param>
    void Register(Type type, Type impl, DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton,bool isAutoInjectProperty = false);
}

而具体实现则须要使用 isAutoInjectProperty 来判断是否须要属性注入功能,下面随便以一个 Register() 方法为例。

public void Register(Type type, DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton,bool isAutoInjectProperty = false)
{
    IocContainer.Register(type,
        ApplyLifestyle(lifeStyle),
        made: Made.Of(FactoryMethod.ConstructorWithResolvableArguments,
            propertiesAndFields: isAutoInjectProperty
                ? PropertiesAndFields.Auto
                : null));
    RegisterTypeEventHandler?.Invoke(this,
        type,
        type);
}

写好以后,咱们再回到 BasicConventionalRegistrar 注册器当中,由于应用服务类型个都是瞬时对象,而且应用服务都会继承 IApplicationService 接口。因此咱们加一个判断,若是是应用服务的话,则在注册的时候,容许进行属性注入。

public class BasicConventionalRegistrar : IConventionalDependencyRegistrar
{
    // ... 其余代码

    public void RegisterAssembly(IConventionalRegistrationContext context)
    {
        // 瞬时对象注册
        var waitRegisterTransient = GetTypes<ITransientDependency>(context.Assembly).ToList();

        foreach (var transientType in waitRegisterTransient)
        {
            if (typeof(IApplicationService).IsAssignableFrom(transientType.ImplType))
            {
                context.IocManager.Register(transientType.ServiceType,transientType.ImplType,DependencyLifeStyle.Transient,true);
                continue;
            }
            
            context.IocManager.RegisterIfNot(transientType.ServiceType,transientType.ImplType,DependencyLifeStyle.Transient);
        }
        
        // ... 其余代码
    }
}

进行了上述更改以后,再次调用接口进行测试能够看到属性已经被正常地注入了。

PS:

这里必定要注意 AspNetCoreApp.Application 库里面的 AspNetCoreAppAppicationModule 模块必定要在 Initialize() 方法调用 IocManager.RegisterAssemblyByConvention(typeof(AspNetCoreAppApplicationModule).Assembly); 不然应用服务不会被注入到 Ioc 容器当中的。

5.2 没法获取拦截器真实类型

该问题主要出在拦截器里面,由于在 DryIoc 当中若是某个类型绑定了多个拦截器,那么就会造成一个层级关系。相似于下面截图的这样:

因此若是你须要在外层的拦截器获取真实对象,目前只能经过递归来解决该问题。

public static Type GetUnproxiedType(object instance)
{
    if (instance is IProxyTargetAccessor proxyTargetAccessor)
    {
        var newInstance = proxyTargetAccessor.DynProxyGetTarget();
        return GetUnproxiedType(newInstance);
    }

    return instance.GetType();          
}

而后使用方式以下:

public void Intercept(IInvocation invocation)
{
    _authorizationHelper.Authorize(invocation.MethodInvocationTarget, TypeExtensions.GetUnproxiedType(invocation.Proxy));
    invocation.Proceed();
}

该问题我在 Github 上面已经向做者提出,做者反馈正在解决。

6、结语

虽然经过文章看起来整个过程十分简单轻松,可是博主当时在操做的时候遇到了很多的坑。结合博主以前关于 Abp 源码分析的文章,你能够更加地了解 Abp 整个框架的结构。

经过这种方式,你除了能够将 DI 框架换成 DryIoc 以外,你也能够替换成你喜欢的其余 DI 框架。

在 Abp vNext 当中的设计Ioc 容器是能够很方便替换的,你能够更加方便地替换 Ioc 容器,就不须要像如今这样麻烦。

PS: 官方就有针对于 AutoFac 与 Castle Windsor 的扩展。

改造完成的代码与 DEMO 的 GitHub 地址:https://github.com/GameBelial/Abp-DryIoc.git

相关文章
相关标签/搜索