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

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

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

这里的重点就是 IServiceScopeFactory 接口,通过名字我们可以得知这是一个工厂,他拥有一个 CreateScope() 方法以创建一个 Scoped 范围。在 MVC 处理请求的时候,通过 CreateScope() 方法获得一个子容器,请求结束之后调用子容器的 Dispose() 方法进行释放。

伪代码大概如下:

public void Request() { var factory = serviceProvider.GetService<IServiceScopeFactory>(); using(var scoped = factory.CreateScope()) { scoped.Resove<HomeController>().Index(); scoped.Resove<TestDbContext>(); } } public class HomeController : Controller { public HomeController(TestDbContext t1) { // 这里的 t1 在 scoped 子容器释放之后会被释放 } public IActionResult Index() { var t2 = IocManager.Instance.Resove<TestDbContext>(); } }

可以看到它通过 using 语句块包裹了 CreateScope() 方法,在 HomeController 解析的时候,其内部的 t1 对象是通过子容器进行解析创建出来的,那么它的生命周期跟随子容器的销毁而被销毁。子容器销毁的时间则是在一次 Http 请求结束之后,那么我们每次请求的时候 t1 的值都会不一样。

而 t2 则有点特殊,因为我们重写 IocManager 类的时候就已经知道这个 Instance 是一个静态实例,而我们在这里通过 Instance 进行解析出来的对象是从这个静态实例的容器当中解析的。这个静态容器是不会随着请求的结束而被释放,因此每次请求得到的 t2 值都是一样的。

2.1 思路与解决方法

思路比较简单,只需要在 IocManager 的 Resolve() 方法进行解析的时候,通过静态容器 IContainer 同样创建一个子容器即可。

更改原来的解析方法 Resolve() ,在解析的时候通过 IocContainer 的 OpenScope() 创建一个新的子容器,然后通过这个子容器进行实例解析。下面是针对 TestApplicationService 的 GetScopedObject() 方法进行测试的结果。

子容器: 351e8576-6f70-4c9b-8cda-02d46a22455d a4af414b-103e-4972-b7e2-8b8b067c1ce1 04bd79d5-33a2-4e2c-87ae-e72f345c4232 Ioc 静态容器: 2e5dfd1f-36d9-4d62-94cd-c6cc66e316ef 2e5dfd1f-36d9-4d62-94cd-c6cc66e316ef 2e5dfd1f-36d9-4d62-94cd-c6cc66e316ef

虽然直接通过 OpenScope() 来构建子容器是可以解决 Scope 对象每次请求都为一个对象的 BUG,但是解析出来的子容器没有调用 Dispose() 方法进行释放。

目前有一个临时的解决思路,即在 IIocManager 增加一个属性字段 ChildContainer ,用于存储每次请求创建的临时 Scope 对象,之后 IocManager 内部优先使用 ChildContainer 进行对象解析。

首先我们来到 IIocManager 接口,为其添加一个 ChildContainer 只读属性与 InitializeChildContainer() 的初始化方法。

public interface IIocManager : IIocRegistrar, IIocResolver, IDisposable { // ... 其他代码 /// <summary> /// 子容器 /// </summary> /// <remarks>本属性的值一般是由 DryIocAdapter 当中创建,而不应该在其他地方进行赋值。</remarks> IResolverContext ChildContainer { get; } /// <summary> /// 初始化子容器 /// </summary> /// <param>用于初始化 IocManager 内部的子容器</param> void InitializeChildContainer(IResolverContext container); }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpyyxg.html