使用 DryIoc 替换 Abp 的 DI 框架

一、背景

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

在各个 Ioc 容器的性能评测当中,DryIoc 以其优异的性能成为我选择使用他的原因。Abp 使用的 Castle Windsor 在解析复杂对象的时候,速度非常慢,而替换为 DryIoc 之后速度可以提升 150% 以上。

【注意】

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

二、准备

你需要准备如下原料:

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

三、分析

首先,Abp 框架的大部分动作基本上都是通过 IIocManager 这个接口对象进行实现的,它抽象了一层作为一个 DI 框架的操作类。它的默认实现是使用的 Castle Windsor 来进行组件的注入与解析,所以我们只需要将其改为使用 DryIoc 的容器其进行操作即可。

其次,在 Abp 框架的很多地方都有用到 Castle Windsor 的 IWindsorContainer 对象,但一般用到该方法的地方都是注入或者绑定组件注册事件,这些我们都可以重新实现的。

做完了以上的工作仅仅是代表我们的 Abp 的所有组件都可以通过 DryIoc 来进行注册和解析,不过要和 ASP.NET Core 集成的话,还需要 IServiceProvider 的适配器,针对于适配器 DryIoc 也给我们提供了,拿来用即可。

所以,我们基本确定了需要变更的项目主要是 Abp 这个核心库,还有 Abp.AspNetCore 这个子模块。除了前面两个比较重要的模块之外,还有 Abp.EntityFrameworkCore 相关的库也需要变更,这是因为他们内部都直接使用到了 IWindsorContainer 对象对容器进行操作的。

四、开撸

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.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 上面已经向作者提出,作者反馈正在解决。

六、结语

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

通过这种方式,你除了可以将 DI 框架换成 DryIoc 之外,你也可以替换成你喜欢的其他 DI 框架。

在 Abp vNext 当中的设计Ioc 容器是可以很方便替换的,你可以更加方便地替换 Ioc 容器,就不需要像现在这样麻烦。

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

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

原文地址:https://www.cnblogs.com/myzony/p/9986947.html

时间: 2024-10-04 11:46:09

使用 DryIoc 替换 Abp 的 DI 框架的相关文章

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

在上一章中主要和大家分享了在ASP.NET Core中如何使用Autofac替换自带DI进行构造函数的批量依赖注入,本章将和大家继续分享如何使之能够同时支持属性的批量依赖注入. 约定: 1.仓储层接口都以“I”开头,以“Repository”结尾.仓储层实现都以“Repository”结尾. 2.服务层接口都以“I”开头,以“Service”结尾.服务层实现都以“Service”结尾. 接下来我们正式进入主题,在上一章的基础上我们再添加一个web项目TianYa.DotNetShare.Core

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

原文:ASP.NET Core Web 应用程序系列(二)- 在ASP.NET Core中使用Autofac替换自带DI进行批量依赖注入(MVC当中应用) 在上一章中主要和大家分享在MVC当中如何使用ASP.NET Core内置的DI进行批量依赖注入,本章将继续和大家分享在ASP.NET Core中如何使用Autofac替换自带DI进行批量依赖注入. PS:本章将主要采用构造函数注入的方式,下一章将继续分享如何使之能够同时支持属性注入的方式. 约定: 1.仓储层接口都以“I”开头,以“Repos

如何在ASP.NET Core应用中实现与第三方IoC/DI框架的整合?

我们知道整个ASP.NET Core建立在以ServiceCollection/ServiceProvider为核心的DI框架上,它甚至提供了扩展点使我们可以与第三方DI框架进行整合.对此比较了解的读者朋友应该很清楚,针对第三方DI框架的整合可以通过在定义Startup类型的ConfigureServices方法返回一个ServiceProvider来实现.但是真的有这么简单吗? 一.ConfigureServices方法返回的ServiceProvider貌似没有用!? 我们可以通过一个简单的

NET Core应用中实现与第三方IoC/DI框架的整合?

NET Core应用中实现与第三方IoC/DI框架的整合? 我们知道整个ASP.NET Core建立在以ServiceCollection/ServiceProvider为核心的DI框架上,它甚至提供了扩展点使我们可以与第三方DI框架进行整合.对此比较了解的读者朋友应该很清楚,针对第三方DI框架的整合可以通过在定义Startup类型的ConfigureServices方法返回一个ServiceProvider来实现.但是真的有这么简单吗? 一.ConfigureServices方法返回的Serv

依赖注入[4]: 创建一个简易版的DI框架[上篇]

本系列文章旨在剖析.NET Core的依赖注入框架的实现原理,到目前为止我们通过三篇文章(<控制反转>.<基于IoC的设计模式>和< 依赖注入模式>)从纯理论的角度对依赖注入进行了深入论述,为了让读者朋友能够更好地理解.NET Core的依赖注入框架的设计思想和实现原理,我们创建了一个简易版本的DI框架,也就是我们在前面文章中多次提及的Cat.我们会上下两篇来介绍这个被称为为Cat的DI框架,上篇介绍编程模型,下篇关注设计实现.[源代码从这里下载] 目录一.DI容器的层

依赖注入[5]: 创建一个简易版的DI框架[下篇]

为了让读者朋友们能够对.NET Core DI框架的实现原理具有一个深刻而认识,我们采用与之类似的设计构架了一个名为Cat的DI框架.在<依赖注入[4]: 创建一个简易版的DI框架[上篇]>中我们介绍了Cat的基本编程模式,接下来我们就来聊聊Cat的设计和实现. 目录一.服务注册:ServiceRegistry 二.DI容器:Cat 三.扩展方法 一.服务注册:ServiceRegistry 由于作为DI容器的Cat对象总是利用预先添加到服务注册来提供对应的服务实例,所以服务注册至关重要.如下

NET Core 3.0 AutoFac替换内置DI的新姿势

原文:NET Core 3.0 AutoFac替换内置DI的新姿势 .NET Core 3.0 和 以往版本不同,替换AutoFac服务的方式有了一定的变化,在尝试着升级项目的时候出现了一些问题. 原来在NET Core 2.1时候,AutoFac返回一个 IServiceProvider 参数注入到ConfigureServices .NET Core 服务中,基本大痣是这样做的. 首先我们需要一个重写 Autofac.Module 的方法,这将用于将我们 Register [数据访问层] 以

&lt;&lt;ABP文档 - 框架&gt;&gt; 1.4 启动配置

文档目录 本节内容: 配置ABP 替换内置服务 配置模块 为一个模块创建配置 ABP在启动时,提供基础框架和模型来配置和模块化. 配置ABP 在预初始化事件中进行配置,示例: public class SimpleTaskSystemModule : AbpModule { public override void PreInitialize() { //为你的应用添加语言 Configuration.Localization.Languages.Add(new LanguageInfo("en

&lt;&lt;ABP文档 - 框架&gt;&gt; 1.5 多租户

文档目录 本节内容: 什么是多租户 多部署 - 多数据库 单部署 - 多数据库 单部署 - 单数据库 单部署 - 混数据库 多部署 - 单/多/混 数据库 ABP中的多租户 启用多租户 宿主与租户 会话 数据过滤 IMustHaveTenant 接口 IMayHaveTenant 接口 补充提醒 在宿主与租户间切换 什么是多租户 维基百科:“软件多租户是一个软件架构,软件只有一个实例运行在服务器,并服务于多个租户.一个租户包含一组用户,他们拥有指定权限,共同访问一个软件实例.一个多租户架构,应用