取消令牌注入 [英] Cancellation Token Injection

查看:88
本文介绍了取消令牌注入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望能够通过依赖项注入传递取消令牌,而不是每次都作为参数传递.这是东西吗?

I'd like to be able to pass cancellation tokens via dependency injection instead of as parameters every time. Is this a thing?

我们有一个asp.net-core 2.1应用程序,在这里我们将来自控制器的调用传递到迷宫般的异步库,处理程序和其他服务中,以满足我们服务的金融科技监管领域的拜占庭式需求.

We have an asp.net-core 2.1 app, where we pass calls from controllers into a maze of async libraries, handlers and other services to fulfil the byzantine needs of the fintech regulatory domain we service.

在请求的顶部,我可以声明我想要取消令牌,然后我将得到一个:

At the top of the request, I can declare that I want a cancellation token, and I'll get one:

    [HttpPost]
    public async Task<IActionResult> DoSomeComplexThingAsync(object thing, CancellationToken cancellationToken) {
        await _someComplexLibrary.DoThisComplexThingAsync(thing, cancellationToken);
        return Ok();
    }

现在,我想成为一名优秀的异步程序员,并确保将我的 cancellationToken 传递给整个调用链中的每个异步方法.我想确保将其传递给EF,System.IO流等.我们具有您期望的所有常用存储库模式和消息传递实践.我们试图使我们的方法简明扼要,并负有单一责任."Fowler"一词显然引起了我的技术主管.因此,我们的类大小和函数体很小,但是我们的调用链非常非常深.

Now, I want to be a good async programmer and make sure my cancellationToken gets passed to every async method down through the call chain. I want to make sure it gets passed to EF, System.IO streams, etc. We have all the usual repository patterns and message passing practices you'd expect. We try to keep our methods concise and have a single responsibility. My tech lead gets visibly aroused by the word 'Fowler'. So our class sizes and function bodies are small, but our call chains are very, very deep.

这意味着每个层,每个功能都必须交出该死的令牌:

What this comes to mean is that every layer, every function, has to hand off the damn token:

    private readonly ISomething _something;
    private readonly IRepository<WeirdType> _repository;

    public SomeMessageHandler(ISomething<SomethingElse> something, IRepository<WeirdType> repository) {
        _something = something;
        _repository = repository;
    }

    public async Task<SomethingResult> Handle(ComplexThing request, CancellationToken cancellationToken) {
        var result = await DoMyPart(cancellationToken);
        cancellationToken.ThrowIfCancellationRequested();
        result.SomethingResult = await _something.DoSomethingElse(result, cancellationToken);
        return result;
    }

    public async Task<SomethingResult> DoMyPart(ComplexSubThing request, CancellationToken cancellationToken) {
        return await _repository.SomeEntityFrameworkThingEventually(request, cancellationToken);
    }

根据我们域复杂性的需要,这是无限的.似乎 CancellationToken 在我们的代码库中出现的次数比任何其他术语都多.即使我们声明了一百万个对象类型,我们的arg列表通常也已经太长(即不止一个).现在,在每个arg列表,每个方法的说明中,都剩下这个多余的取消令牌伙伴.

This goes on ad infinitum, as per the needs of our domain complexity. It seems like CancellationToken appears more times in our codebase than any other term. Our arg lists are often already too long (i.e. more than one) as it is, even though we declare a million object types. And now we have this extra little cancellation token buddy hanging around in every arg list, every method decl.

我的问题是,由于Kestrel和/或管道首先给了我令牌,如果我能拥有这样的东西,那就太好了

My question is, since Kestrel and/or the pipeline gave me the token in the first place, it'd be great if I could just have something like this:

    private readonly ISomething _something;
    private readonly IRepository<WeirdType> _repository;
    private readonly ICancellationToken _cancellationToken;

    public SomeMessageHandler(ISomething<SomethingElse> something, ICancellationToken cancellationToken) {
        _something = something;
        _repository = repository;
        _cancellationToken = cancellationToken;
    }

    public async Task<SomethingResult> Handle(ComplexThing request) {
        var result = await DoMyPart(request);
        _cancellationToken.ThrowIfCancellationRequested();
        result.SomethingResult = await _something.DoSomethingElse(result);
        return result;
    }

    public async Task<SomethingResult> DoMyPart(ComplexSubThing request) {
        return await _repository.SomeEntityFrameworkThingEventually(request);
    }

这将通过DI组合传递出去,当我有一些明确需要令牌的东西时,我可以这样做:

This would then get passed around via DI composition, and when I had something that needs the token explicitly I could do this:

    private readonly IDatabaseContext _context;
    private readonly ICancellationToken _cancellationToken;

    public IDatabaseRepository(IDatabaseContext context, ICancellationToken cancellationToken) {
        _context = context;
        _cancellationToken = cancellationToken;
    }

    public async Task<SomethingResult> DoDatabaseThing() {
        return await _context.EntityFrameworkThing(_cancellationToken);
    }

我疯了吗?我是否只是在每一次该死的时候都传递该死的令牌,并称赞异步神给予的赏金?我应该重新训练成为美洲驼农民吗?他们看起来不错.甚至在问这种异端吗?我现在应该悔改吗?我认为要使async/await正常工作,令牌必须位于func decl中.所以,也许是美洲驼

Am I nuts? Do I just pass the damn token, every damn time, and praise the async gods for the bounty that has been given? Should I just retrain as a llama farmer? They seem nice. Is even asking this some kind of heresy? Should I be repenting now? I think for async/await to work properly, the token has to be in the func decl. So, maybe llamas it is

推荐答案

我认为您的想法很好,我不认为您需要后悔或悔改,这是一个好主意,我也对此进行了思考,然后我解决了

I think you are thinking in a great way, I do not think you need to regret or repent, this is a great idea, I thought also about it, and I come to a solution

public abstract class RequestCancellationBase
{
    public abstract CancellationToken Token { get; }

    public static implicit operator CancellationToken(RequestCancellationBase requestCancellation) =>
        requestCancellation.Token;
}


public class RequestCancellation : RequestCancellationBase
{
    private readonly IHttpContextAccessor _context;

    public RequestCancellation(IHttpContextAccessor context)
    {
        _context = context;
    }

    public override CancellationToken Token => _context.HttpContext.RequestAborted;
}

注册应该是这样的

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<RequestCancellationBase, RequestCancellation>();

现在您可以在任意位置注入 RequestCancellationBase
更好的是,您可以将其直接传递给需要 CancellationToken 的每个方法,这是因为公共静态隐式运算符CancellationToken(RequestCancellationBase requestCancellation)

now you can inject RequestCancellationBase wherever you want,
and the better thing is that you can directly pass it to every method that expects CancellationToken this is because of public static implicit operator CancellationToken(RequestCancellationBase requestCancellation)

此解决方案对我有帮助,希望它对您也有帮助

this solution helped me, hope it is helpful for you also

这篇关于取消令牌注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆