关于 Abp 替换了 DryIoc 框架之后的问题

 在之前有些过一篇文章 

可以看到拦截器不像原来那样是多个层级的情况,而是直接注入到代理类当中。

通过 invocation 参数,我们也可以直接获取到被代理对象的真实类型。

2. 问题 2

2.1 现象与原因

问题 2 则是由于 DryIoc 的 Adapter 针对于 Scoped 生命周期对象的处理不同而引起的,比较典型的情况就是在 Startup 类当中使用 IServiceCollection.AddDbContxt<TDbContext>() 方法注入了一个 DbContext 类型,因为其方法内部默认是使用 ServiceLifeTime.Scoped 周期来进行注入的。

public static IServiceCollection AddDbContext<TContextService, TContextImplementation>(     [NotNull] this IServiceCollection serviceCollection,     [CanBeNull] Action<DbContextOptionsBuilder> optionsAction = null,     ServiceLifetime contextLifetime = ServiceLifetime.Scoped,     ServiceLifetime optionsLifetime = ServiceLifetime.Scoped)     where TContextImplementation : DbContext, TContextService     => AddDbContext<TContextService, TContextImplementation>(         serviceCollection,         optionsAction == null             ? (Action<IServiceProvider, DbContextOptionsBuilder>)null             : (p, b) => optionsAction.Invoke(b), contextLifetime, optionsLifetime);

按照正常的逻辑,一个 Scoped 对象的生命周期应该是与一个请求一致的,当请求结束之后该对象被释放,而且在该请求的生命周期范围内,通过 Ioc 容器解析出来的 Scoped 对象应该是同一个。如果有新的请求,则会创建一个新的 Scoped 对象。

但是使用 DryIoc 替换了原有 Abp 容器之后,现在如果在一个控制器方法当中解析一个 Scoped 周期的对象,不论是几次请求获得的都是同一个对象。因为这种现象的存在,在 Abp 的 UnitOfWorkBase 当中完成一次数据库查询操作之后,会调用 DbContext 的 Dispose() 方法释放掉 DbContext。这样的话,在第二次请求因为获取的是同一个 DbContext,这样的话就会抛出对象已经被关闭的异常信息。

除了开发人员自己注入的 Scoped 对象,在 Abp 的 Zero 模块内部重写了 Microsoft.Identity 相关组件,而这些组件也是通过 IServiceCollection.AddScoped() 方法与 IServiceCollection.TryAddScoped() 进行注入的。

public static AbpIdentityBuilder AddAbpIdentity<TTenant, TUser, TRole>(this IServiceCollection services, Action<IdentityOptions> setupAction)     where TTenant : AbpTenant<TUser>     where TRole : AbpRole<TUser>, new()     where TUser : AbpUser<TUser> {     services.AddSingleton<IAbpZeroEntityTypes>(new AbpZeroEntityTypes     {         Tenant = typeof(TTenant),         Role = typeof(TRole),         User = typeof(TUser)     });      //AbpTenantManager     services.TryAddScoped<AbpTenantManager<TTenant, TUser>>();      //AbpEditionManager     services.TryAddScoped<AbpEditionManager>();      //AbpRoleManager     services.TryAddScoped<AbpRoleManager<TRole, TUser>>();     services.TryAddScoped(typeof(RoleManager<TRole>), provider => provider.GetService(typeof(AbpRoleManager<TRole, TUser>)));      //AbpUserManager     services.TryAddScoped<AbpUserManager<TRole, TUser>>();     services.TryAddScoped(typeof(UserManager<TUser>), provider => provider.GetService(typeof(AbpUserManager<TRole, TUser>)));      //SignInManager     services.TryAddScoped<AbpSignInManager<TTenant, TRole, TUser>>();     services.TryAddScoped(typeof(SignInManager<TUser>), provider => provider.GetService(typeof(AbpSignInManager<TTenant, TRole, TUser>)));          // ... 其他注入代码      return new AbpIdentityBuilder(services.AddIdentity<TUser, TRole>(setupAction), typeof(TTenant)); }

以上代码与 DbContext 产生的异常现象一致,都会导致每次请求获取的都是同一个对象,而 Abp 在底层会在每次请求结束后进行释放,这样也会造成后续请求访问到已经被释放的对象。

上面这些仅仅是替换 DryIoc 框架后产生的异常现象,具体的原因在于 DryIoc 官方编写的 DryIoc.Microsoft.DependencyInjection 扩展。这是针对于 ASP.NET Core 自带的 DI 框架进行替换的 Adapter 适配器,大体原理就是通过实现 IServiceScopeFactory 接口与 IServiceScope 接口替换掉原有 DI 框架的实现。以实现接管容器注册与生命周期的管理。

这里的重点就是 IServiceScopeFactory 接口,通过名字我们可以得知这是一个工厂,他拥有一个 

50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信