我自己不使用 NHibernate 或 Envers,所以也许只是在这里吐口水。我不认为当前用户会话真的存在于服务范围之外。但是您应该能够传递 Envers 范围。我认为您想要做的是使用 AppHost IoC 将对您的单例实例的引用传递给服务。
在您的AppHost 中设置您的单例实例,然后将其注册到容器中,以便将其注入到每个服务请求中。
通过执行以下操作:
container.Register(c => singletonInstance).ReusedWithin(ReuseScope.None);
您需要扩展 Service 以使用自定义基础:
public class MyServiceBaseWithEnversSupport : Service
{
public EnversSingletonInstanceType Envers { get; set; } // IoC will inject here
}
那么您的处理程序将需要使用这个扩展的自定义 Service 基础,所以像这样:CustomerHandler 只是一个示例,您的服务处理程序会有所不同
public class CustomerHandler : MyServiceBaseWithEnversSupport
{
public object Get(ListCustomers request)
{
// You can then access the instance in the scope of the request
// So you now have access to the current user identity
Envers.Username = Session.Username; // Just an example modify as required.
}
}
您可以自动填充这些值,通过设置自定义 ServiceRunner 来省去在每个操作处理程序中设置值的麻烦。
创建自定义ServiceRunner:
public class ServiceRunner<T> : ServiceStack.ServiceHost.ServiceRunner<T>
{
public ServiceRunner(IAppHost appHost, ActionContext actionContext) : base(appHost, actionContext)
{
}
public override object Execute(IRequestContext requestContext, object instance, T request)
{
// Check if the instance is of type MyServiceBaseWithEnversSupport
var ms = instance as MyServiceBaseWithEnversSupport;
// If the request is not using the MyServiceBaseWithEnversSupport, then allow it to run, as normal.
if(ms == null)
return base.Execute(requestContext, instance, request);
// Access the Envers object, set using the Session Information
ms.Envers.Username = ms.Session.Username;
return base.Execute(requestContext, ms, request);
}
}
通过将其添加到您的AppHost 来配置您的应用程序以使用它:
public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(ActionContext actionContext)
{
return new ServiceRunner<TRequest>(this, actionContext);
}
抱歉,关于 Enver 的单例对象类型以及调用对象以设置数据的正确属性或方法有些模糊,但我假设您可以替换为适当的值。
正如我所说,我对 NHibernate 或 Envers 都不熟悉,所以这可能会关闭,但至少在 ServiceStack 方面可能会有所帮助
更新尝试:
我假设对于您的服务的每个请求都会发生以下情况:
- 应用程序宿主为请求创建一个服务上下文线程。
- 在线程中存在用户会话
- 每个请求都会执行一些 NHibernate 数据库操作。
- 数据库操作可能会触发您的 envers 侦听器中的事件处理程序。您需要填充当前请求的用户名,即在当前线程的范围内。
您是否考虑过创建一个ThreadStaticstatic 变量,该变量对 ServiceStack 和侦听器中的 Envers 事件处理程序都是全局的?
public static class Global
{
[ThreadStatic]
public static string Username;
}
然后在 ServiceStack 中设置 Username 的值在身份验证点,这将发生在您的侦听器的处理程序之前。然后在侦听器处理程序中从Global.Username 读取值。该值是线程安全的,只存在于请求的范围内。
注意:我假设 NHibernate/Envers 在请求线程上运行,并且它不会为每个请求生成其他工作线程。