你說我 Castle Windsor 庫用得好好的,爲啥要大費周章的替換成 DryIoc 庫呢?那就是性能,DryIoc 是一款優秀並且輕量級的 DI 框架,整個項目代碼就兩個文件,加起來代碼 1 萬行左右(PS: 大部分都是註釋)。git
在各個 Ioc 容器的 性能評測 當中,DryIoc 以其優異的性能成爲我選擇使用他的緣由。Abp 使用的 Castle Windsor 在解析複雜對象的時候,速度很是慢,而替換爲 DryIoc 以後速度能夠提高 150% 以上。github
【注意】數據庫
本文僅對 .NET Core 相關的庫進行更改並測試,.NET Framework 相關的庫並無進行修改測試。數組
你須要準備以下原料:session
首先,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
對象對容器進行操做的。函數
Abp 自己庫裏面須要改動的地方基本集中在 Dependency 文件夾裏面,這個文件夾咱們以前有講過,基本全部依賴注入相關的類型與接口都存放在這裏面的。源碼分析
除了依賴注入相關的類型須要更改之外,咱們還須要更改各個攔截器注入的地方。由於在以前 Abp 若是須要爲某個類型注入攔截器的話,是使用到了 IWindsorContainer
接口所提供的組件注入事件來進行攔截器注入的。
首先咱們針對於 Abp 庫添加 DryIoc 庫的 NuGet 包引用,這裏我是安裝的 3.1.0-preview-06 版本。
首先看一下 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[]
,固然也由於變動了方法簽名,因此咱們須要更改 ScopedIocResolver
、IIocResolver
、IocResolverExtensions
定義裏面帶參數的解析方法簽名。
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); } }
Abp 庫自己提供了兩個接口 (ITransientDependency
、ISingletonDependency
) 來幫助用戶快速地注入某個對象,而後經過註冊規約結合 IocManager
提供的 AddConventionalRegistrar()
方法和 RegisterAssemblyByConvention()
方法可以快速地將某個程序集內部符合規則的類型進行注入。(PS: 這裏其實流程很像以前 Installer 的作法)
在使用 Castle Windsor 的時候,Abp 自己並不須要作太多的工做,就能夠實現上述的功能。而 DryIoc 自己是沒有提供這些比較高級的特性的,但原理其實並不複雜, 就是掃描整個程序集的全部類型,而後挨個進行判斷便可。
在原來的 BasicConventionalRegistrar
類型內部,對實現了 ITransientDependency
、 ISingletonDependency
、 IInterceptor
接口的類型進行了自動註冊。因此咱們就有了如下的實現代碼:
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
接口所提供的註冊方法。
由於沒有使用 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); } }
這個擴展類的用法,在後面就有體現。
最開始 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; } }
以後呢,咱們在 IocManager
的 Register()
註冊方法內部都顯式地觸發這個事件。
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); } } }
按照上面這種步驟,完成剩餘攔截器註冊器的更改。
若是上述操做都已經完成了的話,那麼基本上只剩下 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); } }
作完以上變動以後,新建一個控制檯程序,引用這個 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 { } }
針對 Abp 庫進行測試以後,基本上咱們 Abp 如今全部組件都是經過 DryIoc 來進行註冊與解析的了。不過僅僅針對 Abp 作這些更改實際上是不夠的,除了 Abp 核心庫以外,咱們最經常使用的就是數據庫操做了。由於在 Abp.EntityFrameworkCore 庫 和 Abp.EntityFramework.Common 的內部也有部分代碼在以前是直接經過 IWindsorContainer
進行註冊與解析操做的,因此咱們也得繼續改報錯的地方。
在 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 應該能夠正常地進行編譯了。
在 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)); } } }
該錯誤在 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); } }
這個解析器的主要問題則與前面的不同,這裏報錯是由於在構造 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 相關的庫改造就已經成功完成了。
到目前,咱們已經針對 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(); }
除了更改上述問題以外,在 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(庫項目) ,而且配置好各自的依賴關係。
首先咱們更改 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 項目,咱們能夠看到正常運行了。
在示例項目當中,我在 AspNetCoreApp.Application 庫當中創建了一個 TestApplicationService
服務,該服務用有一個 GetJson()
方法。
在其內部,我調用了父類提供的 AbpSession
屬性,按照正常的狀況下,該屬性的實現應該是 ClaimsAbpSession
類型,不過經過測試以後我獲得瞭如下結果:
能夠看到,它填充的是默認的空實現,形成這個問題的緣由是,DryIoc 自己在註冊對象的時候,須要顯式提供屬性注入的選項,不然默認是不啓用屬性注入的。
鑑於此,咱們爲 IIocRegistrar
與 IocManager
內所提供的 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 容器當中的。
該問題主要出在攔截器裏面,由於在 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