简体   繁体   English

Blazor 页面中的 CancellationToken?

[英]CancellationToken in Blazor Pages?

After living under a rock for 2 years employment wise, I am now confronted with Blazor at my new Workplace and have a lot of catch up to do after doing mostly ASP.NET Framework MVC prior to the 2 Years.在明智地工作了 2 年之后,我现在在我的新工作场所遇到了 Blazor,并且在 2 年之前主要完成了 ASP.NET 框架 MVC 之后,还有很多工作要做。

Trying myself on Blazor server side, I tried to apply my past knowledge which included cancellationtokens for async operations and i couldn't find much information about them in combination with Blazor.在 Blazor 服务器端尝试自己,我尝试应用我过去的知识,包括用于异步操作的取消令牌,但我找不到与 Blazor 结合使用的太多信息。

Are they still a Best Practice or did they became Obsolete at some point?它们仍然是最佳实践,还是在某个时候过时了? I did found this previously asked question which recommends creating a tokensource on the OnInitializedAsync() method and cancelling it on Dispose() which i honestly find a bit crude.我确实发现了这个先前提出的问题,它建议在OnInitializedAsync()方法上创建一个令牌源并在Dispose()上取消它,老实说我觉得有点粗略。 (I would need to implement this for each page and you know... DRY) (我需要为每个页面实现这个,你知道......干)

I also found this Article about advanced Scenarios on Microsoft Docs that explains how to implement a Circuit Handler, which honestly is a bit beyond me right now and most likely way out of scope for my little home-project.我还在Microsoft Docs 上找到了这篇关于高级场景的文章,它解释了如何实现电路处理程序,老实说,这现在有点超出我的能力,而且对于我的小家庭项目来说,很可能超出 scope 的范围。

In comparison, in asp.net Framework MVC i would build a Controller like this:相比之下,在 asp.net 框架 MVC 中,我将构建一个 Controller,如下所示:

namespace SampleWebsite.Controllers
{
    public class SampleController : ApiController
    {
        private readonly MyEntities _entities = new MyEntities();

        public async Task<IHttpActionResult> MyAsyncApi(CancellationToken cancellationToken)
        {
            var result = _entities.MyModel.FirstOrDefault(e => e.Id == 1, cancellationToken: cancellationToken);
            return OK(result);
        }
    }
}

The CancellationToken will be injected by asp.net Framework / Core and is directly linked to the current context connection-pipe. CancellationToken 将由 asp.net 框架/核心注入,并直接链接到当前上下文连接管道。 Hence, if the user closes the connection, the token becomes invalid.因此,如果用户关闭连接,令牌就会失效。

I would have assumed that for asp.net core and blazor where dependency-injections is a big part of it, this would be the case here too, but i could not find any documentation about this here.我会假设对于 asp.net 内核和 blazor 依赖注入是其中的重要组成部分,这也是这里的情况,但我在这里找不到任何关于此的文档。

So, should cancellationtokens still be used at this point or does Microsoft do some magic in the background for asynchronous tasks?那么,此时是否仍应使用取消令牌,或者微软是否在后台为异步任务做了一些魔术? And if yes, what would be the best implementation?如果是,最好的实施方式是什么?

EDIT: Here would be my Setup to clarify:编辑:这将是我的设置来澄清:

The Blazor-Component: Blazor 组件:

@page "/Index"
@inject IIndexService Service

@* Some fancy UI stuff *@

@code {
    private IEnumerable<FancyUiValue> _uiValues;

    protected override async Task OnInitializedAsync()
    {
        _uiValues = await Service.FetchCostlyValues();
    }
}

And the Injected Service-Class that does the heavy lifting:以及完成繁重工作的注入服务类:

public interface IIndexService
{
    Task<IEnumerable<FancyUiValue>> FetchCostlyValues();
}

public class IndexService : IIndexService
{
    public async Task<IEnumerable<FancyUiValue>> FetchCostlyValues()
    {
        var uiValues = await heavyTask.ToListAsync(); // <-- Best way to get a cancellationtoken here?
        return uiValues;
    }
}

My question is, what would be the best way to get a token in the specificed part of the code or would it be irrelevant because the Server would kill all running tasks when the connection (as example) ends?我的问题是,在代码的特定部分中获取令牌的最佳方法是什么,或者它是否无关紧要,因为服务器会在连接(例如)结束时终止所有正在运行的任务?

A CancellationToken is a best-practice on the server but you don't usually need one in the UI layer. CancellationToken 是服务器上的最佳实践,但您通常不需要在 UI 层中使用它。

The question you linked to talks about a 'long running background task', something that when needed shouldn't be part of the UI layer.您链接到的问题是关于“长时间运行的后台任务”,在需要时不应该成为 UI 层的一部分。

When you do need a token then create a Source with a using(...) {...} around that specific action.当您确实需要一个令牌时,然后using(...) {...}围绕该特定操作创建一个 Source。 The lifetime of the source is not related to that of your page so the page Dispose() is not the right place.源的生命周期与您的页面的生命周期无关,因此页面 Dispose() 不是正确的位置。


Your own example case:您自己的示例案例:

protected override async Task OnInitializedAsync()
{
    _uiValues = await Service.FetchCostlyValues();
}

This will either succeed or throw an exception.这将成功或引发异常。 The only use for a cancelToken would be one with a timeout limit. cancelToken 的唯一用途是具有超时限制。

protected override async Task OnInitializedAsync()
{
   using (var source = new CancelationTokenSource(100))
   {
     _uiValues = await Service.FetchCostlyValues(source.Token);
   }
}

or does Microsoft do some magic in the background for asynchronous tasks?还是微软在后台为异步任务做了一些魔术?

No.不。

After 2 years of experience with Blazor, i figured that the only reliable way to pass an CancellationToken to a Task within an Object of a longer Lifetime (eg Singleton or Scoped Service) is the combination of IDisposeable and CancellationTokenSource在 Blazor 的 2 年经验之后,我发现将CancellationToken传递给具有更长生命周期的 Object 内的任务的唯一可靠方法(例如CancellationTokenSource的源组合或可取消服务令牌)是IDisposeable或可取消服务令牌的来源组合

@page "/"
@implements IDisposable

*@ Razor Stuff *@

@code
{
    private CancellationTokenSource _cts = new();

    protected override async Task OnInitializedAsync()
    {
        await BusinessLogicSingleton.DoExpensiveTask(_cts.Token);
    }

    #region IDisposable

    public void Dispose()
    {
        _cts.Cancel();
        _cts.Dispose();
    }

    #endregion
}

On repeated use or just to comply to the DRY-Rule, you can also inherit from the ComponentBase Class and then use that Class for your Components that require to pass a CancellationToken :在重复使用或只是为了遵守 DRY-Rule 时,您还可以从ComponentBase Class 继承,然后将 Class 用于需要传递CancellationToken的组件:

public class CancellableComponent : ComponentBase, IDisposable
    {
        internal CancellationTokenSource _cts = new();

        public void Dispose()
        {
            _cts.Cancel();
            _cts.Dispose();
        }
    }
@page "/"
@inherits CancellableComponent

@* Rest of the Component *@

I also found that while you could Inject the IHttpContextAccessor and use the HttpContext.RequestAborted token which is the same that will be generated and injected in your ASP.Net MVC Method Calls, as of the current .Net6 Version it will never fire even after the Connection to the Client is severed and the providing HttpContext is disposed.我还发现,虽然您可以注入IHttpContextAccessor并使用HttpContext.RequestAborted令牌,这与将在您的 ASP.Net MVC 方法调用中生成和注入的令牌相同,但从当前的.Net6版本开始,即使在与客户端的连接被切断,提供的HttpContext被释放。

This may be a case for the Developer-Team on Github as i do see UseCases for it where the User is allowed to exit the Component while the Task keeps on going until the User leaves the Website completely.这可能是 Github 上的开发团队的情况,因为我确实看到了 UseCases,其中允许用户退出组件,而任务继续进行,直到用户完全离开网站。
(For such cases, my recommended Workaround would be to write your own CircuitHandler that will give you Events for when a Circuit is removed.) (对于这种情况,我推荐的解决方法是编写您自己的CircuitHandler ,它会在删除电路时为您提供事件。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM