我自己不使用 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 侦听器中触发事件处理程序。您需要填充当前请求的用户名,即在当前线程的范围内。
您是否考虑过在侦听器中创建一个对 ServiceStack 和 Envers 事件处理程序都是全局的变量?ThreadStatic
static
public static class Global
{
[ThreadStatic]
public static string Username;
}
然后在 ServiceStack 中设置Username
Authentication 点的值,这将发生在侦听器的处理程序之前。然后在侦听器处理程序中从Global.Username
. 该值是线程安全的,并且只存在于请求的范围内。
注意:我假设 NHibernate/Envers 在请求线程上运行,并且它不会为每个请求生成其他工作线程。