【问题标题】:ServiceStack: Reinstate pipeline when invoking a Service manually?ServiceStack:手动调用服务时恢复管道?
【发布时间】:2020-10-27 20:53:58
【问题描述】:

作为this question 的后续行动,我想了解如何改进手动调用服务。这比我想要的要长,但我觉得需要背景信息。

在进行 pub/sub(广播)时,不使用 Messaging API 中的正常顺序和流程,而是在收到 pub/sub 消息时使用 IRedisClient、IRedisSubscription 获得回调:

_subscription.OnMessage = (channel, msg) =>
{
    onMessageReceived(ParseJsonMsgToPoco(msg));
};

然后,Action onMessageReceived 将依次调用正常的 .NET/C# 事件,如下所示:

protected override void OnMessageReceived(MyRequest request)
{
    OnMyEvent?.Invoke(this, new RequestEventArgs(request));
}

这行得通,我收到了我的请求和所有这些,但是,我希望将它简化到另一个流程中,即消息 API 中的流程,这意味着,请求找到了进入 Service 类实现的方式,并且所有正常的样板和依赖注入都会像使用 Messaging API 一样发生。

因此,在我的事件处理程序中,我手动调用服务:

private void Instance_OnMyEvent(object sender, RequestEventArgs e)
{
    using (var myRequestService = HostContext.ResolveService<MyRequestService>(new BasicRequest()))
    {
        myRequestService.Any(e.Request);
    }
}

确实找到了 MyRequestService 并调用了 Any ,并且依赖注入适用于服务。

问题一:

  • OnBeforeExecuteOnAfterExecute等方法不会被调用,除非我手动调用它们,比如:myRequestService.OnBeforeExecute(e)等。管道的哪些部分丢失了?能否以某种简单的方式恢复它,这样我就不必按顺序手动调用它们?

问题 2:

我认为我这样做是在搞砸 DI 系统:

using (var myRequestService = HostContext.ResolveService<MyRequestService>(new BasicRequest()))
{
    myRequestService.OnBeforeExecute(e.Request);
    myRequestService.Any(e.Request);
    myRequestService.OnAfterExecute(e.Request);
}

我看到的效果是,我使用container.AddScoped 注册的注入依赖项没有作用域,但似乎是静态的。我看到这一点是因为我在注入的类中有一个 Guid,在这种情况下 Guid 总是相同的,而每个请求的 Guid 应该不同。

container.AddScoped<IRedisCache, RedisCache>();

OnBeforeExecute(Service 的后代)就像:

public override void OnBeforeExecute(object requestDto)
{
    base.OnBeforeExecute(requestDto);
    IRedisCache cache = TryResolve<IRedisCache>();
    cache?.SetGuid(Guid.NewGuid());
}

因此,IRedisCache Guid 每次都应该不同,但事实并非如此。但是,当我“从头到尾”使用消息传递 API 时,这可以正常工作。看来,如果我在 AppHostBase 后代中调用 TryResolve,则会忽略 AddScoped,并将一个实例放置在容器中,然后永远不会删除。

【问题讨论】:

    标签: redis servicestack publish-subscribe


    【解决方案1】:

    管道的哪些部分丢失了?

    request pipeline 没有一个被执行:

    myRequestService.Any(e.Request);
    

    实际上只是调用MyRequestService 类的Any C# 方法,它不(也不能)做任何其他事情。

    在服务请求期间调用其他服务的推荐方法是使用Service Gateway

    但是,如果您想在 HTTP 请求之外调用服务,您可以使用 RPC Gateway 来执行不受信任的服务,因为它会调用完整的请求管道并将 HTTP 错误响应转换为类型化错误响应:

    HostContext.AppHost.RpcGateway.ExecuteAsync()
    

    要在服务请求之外执行内部/受信任的服务,您可以使用 ServiceStack MQ 使用的 HostContext.AppHost.ExecuteMessage,它应用消息请求请求/响应过滤器、服务操作过滤器和事件。

    我已经注册了 container.AddScoped

    不要使用Request Scoped dependencies outside of a HTTP Request,如果依赖项是 ThreadSafe 则使用 Singleton,否则将它们注册为 Transient。如果您需要传递每个请求的存储,请将它们传递到 IRequest.Items

    【讨论】:

    • 感谢您的回答,我会看看是否可以使用您的指导解决此问题。我也意识到Any 方法不会做任何事情,但我认为HostContext.ResolveService 可能会做一些魔术。
    • “不要在 HTTP 请求之外使用请求范围的依赖项”。就我而言,依赖项在 AppHostBase 后代中注册,并由服务使用(使用消息传递 API)。所以,据我了解,我不能真正选择单例,因为有时请求通过消息传递 API 进入,有时通过 pub/sub 进入,如上所述。因此,它是在考虑消息传递 API 的情况下构建和完成的,我希望将 sub/pub 方法简化为现有的和正确的实现。无论如何,我将使用您的提示,看看它是如何工作的。谢谢。
    • @Ted All ResolveService&lt;T&gt; 所做的是从 IOC 解析一个新的服务实例并注入 IRequest 上下文。您只能在 AppHost 或 ConfigureServices() Startup 类中注册 Startup 的依赖项,但如果您需要在 HTTP 请求之外执行服务,则不应使用 Request Scoped 依赖项。
    • 好吧,在普通的 Messaging API 中,每个请求都需要一个实例,因为我们有“跟踪器”(用于跟踪请求期间发生的事情),并且需要为每个请求创建它,因此,AddScoped 似乎是一种自然的方法。但是正如您所解释的,如果您仅使用 Messaging API,则此方法有效,但如果我们超出请求(它不是常规的 HTTP API,它通过/通过 Redis)则无效。但是,瞬态似乎是错误的,每次访问接口属性时都会创建新实例?我认为,那将意味着失去“状态”。嗯。
    • 跟进:在这种情况下,使用HostContext.AppHost.ExecuteMessage(theMessage); 对我来说效果很好;将其“挂钩”到请求管道中并让所有 DI 按预期工作。即使在 AppHostBase 后代中,当在属于管道的方法中执行此操作时,我也会从 TryResolve 获得正确的实例,例如在 OnAfterExecute 中,这对我来说已经足够了!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多