具有每个请求生存期范围的 ServiceStack 自承载应用程序
本文关键字:ServiceStack 应用程序 范围 请求 生存期 | 更新日期: 2023-09-27 18:25:33
使用ServiceStack,我一直在自托管Web应用程序中解决对象生存期管理的问题。
我的要求:
- 需要每个请求对象的生存期范围。
- 我正在使用Castle Windsor IoC和已实现的ServiceStack IoC适配器。
- 我的应用程序是自托管的,具有基类AppHostHttpListenerPoolBase (ServiceStack v4(
- 可能有一天我想继续使用 IIS,因此它必须是灵活的。
一般问题:
CastleWindsor 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 之后创建新作用域(如果尚未初始化(。
问题:
- 难以处置。释放_currentLifetimeScope的最佳方法是实现自定义 IServiceRunner(或从 ServiceRunner 继承(覆盖 AfterEachRequest 方法。但我并不完全知道 AfterEachRequest 是否实际上是在请求线程中执行的。
- 迁移到 IIS 可能会导致一些问题,因为据我所知,IIS 不保证标头和请求上下文之间的不可更改绑定。
使用 IRequest 实例
在MyScopeAccessor中,我只是存储
private static readonly ConcurrentDictionary<IRequest, ILifetimeScope> LifetimeScopes;
并在相应的自定义 ServiceRunner 方法(OnBeforeEachRequest、OnAfterEachRequest(中创建和释放当前生存期范围。
问题:
- 我不知道如何从 GetScope 全局访问当前请求,MyScopeAccessor 对服务和请求一无所知。
此外,如果ServiceStack默认的Funq IoC解决了这个问题,这很有趣。
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*/ }
}