簡體   English   中英

我是否應該通過 asp.net 核心 3.1 web ZDB974238D704A 中的所有 class 方法檢測取消令牌?

[英]Should I plumb Cancellation Tokens through all of my class methods in an asp.net core 3.1 web API?

我已經在 asp.net 核心 3.1 中構建了一個 web 服務器一段時間,並且我開始注意到(坦率地說,不喜歡)我通過我的應用程序管道取消令牌。 我想出了這個“問題”的解決方案(它是,它不是)並且正在尋求意見。

首先,我們都在同一個頁面上 - 讓我們回顧一下請求管道:

  • 請求到達服務器,asp.net 核心框架實例化一個HttpContext
  • 框架實例化一個CancellationToken並將其附加到HttpContext.RequestAborted屬性(是的,大聲笑,具有CancellationToken類型)
  • 一些映射發生在框架的幕后,從 DI 容器中解析出 controller(控制器注冊為服務)
  • 調用 controller 方法,由傳遞HttpContext實例的中間件管道裝飾
  • 框架對HttpContext實例執行 model 綁定(取自路由參數、查詢參數、正文、header 等),然后嘗試將結果傳遞給映射期間發現的方法
  • 符合簽名的 Model 綁定結果被注入(或通過,無論您喜歡哪個詞)以及從HttpContext.RequestAborted屬性中獲取的 CancellationToken。

第二 - 這是我的問題的一些背景:

我使用 Autofac 進行依賴注入,並且我在某些時候了解到您可以提供依賴項(類實例)Lifetime Scopes,這將決定 class 實例的壽命。 我還開發了一個實用或直觀的視角,有點不同,也因 scope 類型而異。 例子可能最好地說明了這一點:

  • 應用程序生命周期的單個實例(您可以在其中存儲數據,然后隨時在應用程序的其他任何地方解析它),
  • 每次注入類型時都有一個唯一的實例,因此注入的實例之間不會共享 state
  • 其他!

由於這是一個 asp.net 核心 3.1 mvc/web api,因此 Autofac 提供了一個lifetimeScope,它允許您從當前請求的上下文中解析來自容器的相同實例(換句話說,為任何給定請求執行的任何代碼將解決相同的實例。不同的請求?不同的實例)。 這是 Autofac 6 中的.InstancePerLifetimeScope() scope 類型。

使用這些信息,我意識到我可以編寫一個中間件,將httpContext.RequestAborted取消令牌分配給傳輸 class,該傳輸具有InstancePerLifetimeScope的生命周期 scope,然后將該傳輸注入直接使用該令牌的地方。 在代碼庫中,這不是很多地方(當然遠少於所有探測到的位置)。

這是一個最小的代碼集,演示了這種無鉛場景的設置可能是什么樣的——想象每個 class 都在自己的文件中:

public class SetCancellationTokenTransportMiddleware
{
    private readonly RequestDelegate next;

    public SetCancellationTokenTransport(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task InvokeAsync(HttpContext context, ITransportACancellationToken transport)
    {
        transport.Assign(context.RequestAborted);
        await next(context);
    }
}



// Autoface module registrations
public class GeneralModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<SomeDependency>().As<ISomeDependency>();
        builder.RegisterType<Repository>().As<IRepository>();
        builder.RegisterType<CancellationTokenTransport>().As<ITransportACancellationToken>().InstancePerLifetimeScope();
    }
}

// mvc controller - cancellation token is not injected
public class Controller
{
    private readonly ISomeDependency dep;

    public Controller(ISomeDependency dep)
    {
        this.dep = dep;
    }

    [HttpGet()]
    public async Task<Thing> Get()
    {
        return await dep.Handle();
    }
}

// This gets bypassed -- the cancellation token isn't plumbed through the handle method
public class SomeDependency : ISomeDependency
{
    private readonly Repository repository;
    
    public SomeDependency(IRepository repository)
    {
        this.repository = repository;
    {

    public async Task Handle() // no token is passed
    {
        return await repository.GetThing(); // no token is passed
    }
}

public class Repository : IRepository
{
    private readonly ITransportACancellationToken transport;
    private readonly DbContext context;

    public Repository(ITransportACancellationToken transport, DbContext context)
    {
        this.transport = transport;
        this.context = context;
    }

    public async Task<Thing> GetThing()
    {
        // The transport passes the token here - bypassing the calling class[es]

        return await context.Things.ToListAsync(transport.CancellationToken); 
    }
}

所以我的問題一般是 - 你如何看待這種直接向代幣消費者(而不是他們的中介)提供取消代幣的方法? 你能想到什么優點和缺點? 你有沒有被這種方法燒過? 有沒有人在他們的應用程序或他們工作過的應用程序中使用它?

我能想到的反對這種模式的一個論點是您需要明確剝奪取消令牌的代碼路徑的情況。 但是,這里的存儲庫在技術上也可以設計為允許人們選擇是否允許取消。 使用工作單元模式,這也得到了部分緩解(傳輸 class 將被注入工作單元)。

我非常想聽聽一些有經驗的觀點。 當然也歡迎一般的想法。

你能想到什么優點和缺點?

唯一的優點是您的樣板代碼更少。

主要的缺點是您還需要了解並掛鈎對令牌的所有所需更改。 如您所述,某些代碼路徑應忽略取消。 在某些代碼路徑中使用替代標記也很常見,例如,基於 Polly 的重試/計時器處理程序。 為了處理那些更復雜的情況,您的傳輸類型應該包含一個不可變的 CT 堆棧,而不僅僅是一個值。

CT 流也可能(理論上)不能完全模仿異步方法流,這些傳輸類型(在后台使用AsyncLocal<T> )本質上與之相關。 不過,這將是一種非常罕見的情況。

第二個缺點是任何類型的傳輸都是一種“魔法”,增加了閱讀代碼的心理負擔。

有沒有人在他們的應用程序或他們工作過的應用程序中使用它?

不是在我工作過的應用程序中,但我以前見過這個想法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM