具有每个请求生存期范围的 ServiceStack 自承载应用程序

本文关键字:ServiceStack 应用程序 范围 请求 生存期 | 更新日期: 2023-09-27 18:25:33

使用ServiceStack,我一直在自托管Web应用程序中解决对象生存期管理的问题。

我的要求:

  1. 需要每个请求对象的生存期范围。
  2. 我正在使用Castle Windsor IoC和已实现的ServiceStack IoC适配器。
  3. 我的应用程序是自托管的,具有基类AppHostHttpListenerPoolBase (ServiceStack v4(
  4. 可能有一天我想继续使用 IIS,因此它必须是灵活的。

一般问题:

Castle

Windsor IoC 实现了自己的每请求生存期策略,但它绑定到 http 模块,因此它仅适用于 IIS 托管的应用程序。 因此,我必须实现我的自定义 IScopeAccessor(由 Castle Windsor 提供(来处理对象生存期。这里的问题是没有可以用来绑定到当前请求的钩子。

鉴于

public class MyScopeAccessor : IScopeAccessor
{
    public ILifetimeScope GetScope(CreationContext context)
    {
       //implement it
    }
}

我必须实现 GetScope 方法。

有两个主要想法我无法完成:

使用 [线程静态]

在MyScopeAccessor中,我只是存储

[ThreadStatic] 
private static ILifetimeScope _currentLifetimeScope;

并在第一个 GetScope 之后创建新作用域(如果尚未初始化(。

问题:

  1. 难以处置。释放_currentLifetimeScope的最佳方法是实现自定义 IServiceRunner(或从 ServiceRunner 继承(覆盖 AfterEachRequest 方法。但我并不完全知道 AfterEachRequest 是否实际上是在请求线程中执行的。
  2. 迁移到 IIS 可能会导致一些问题,因为据我所知,IIS 不保证标头和请求上下文之间的不可更改绑定。

使用 IRequest 实例

在MyScopeAccessor中,我只是存储

private static readonly ConcurrentDictionary<IRequest, ILifetimeScope> LifetimeScopes;

并在相应的自定义 ServiceRunner 方法(OnBeforeEachRequest、OnAfterEachRequest(中创建和释放当前生存期范围。

问题:

  1. 我不知道如何从 GetScope 全局访问当前请求,MyScopeAccessor 对服务和请求一无所知。

此外,如果ServiceStack默认的Funq IoC解决了这个问题,这很有趣。

具有每个请求生存期范围的 ServiceStack 自承载应用程序

Funq 确实处理 RequestScoped 依赖项,该依赖项将请求上下文依赖项存储在RequestContext.Instance.Items[]字典中。

任何一次性用品都可以在请求结束时自动处置RequestContext.Instance.TrackDisposable()注册。

在每个请求结束时,将触发AppHost.OnEndRequest(),该请求将遍历并释放存储在该请求的 RequestContext 中的任何依赖项。

如果您的 Windsor ContainerAdapter 实现了 IRelease 接口,则会自动调用它以释放任何可以自行处理的实例。如果要更改默认行为,这两个 API 都可以在AppHost中覆盖:

public virtual void OnEndRequest()
{
    var disposables = RequestContext.Instance.Items.Values;
    foreach (var item in disposables)
    {
        Release(item);
    }
    RequestContext.Instance.EndRequest();
}
public virtual void Release(object instance)
{
    try
    {
        var iocAdapterReleases = Container.Adapter as IRelease;
        if (iocAdapterReleases != null)
        {
            iocAdapterReleases.Release(instance);
        }
        else
        {
            var disposable = instance as IDisposable;
            if (disposable != null)
                disposable.Dispose();
        }
    }
    catch { /*ignore*/ }
}