簡體   English   中英

在 C# 中,使用貸款模式、TryAsync 和 RestClient.ExecuteAsync(),如何讓系統等待第二次標注的結果?

[英]In C#, using a loan pattern, TryAsync, and RestClient.ExecuteAsync(), how can I get the system to wait for the result of the second callout?

我目前正在重構一個使用 RestSharp 的RestClient調用 Personio 的微服務,以便使用最新版本的 RestSharp (v107),以及使用ExecuteAsync而不是Execute

我有以下方法:

    [SuppressMessage("Style", "IDE0053:Use expression body for lambda expressions", Justification = "lambda leave Match ambiguous.")]
    public TryAsync<T> WithAuthorization<T>(Func<Token, Task<T>> doSomething, CancellationToken cancellationToken) =>
        TryAsync(async () =>
        {
            T? result = default;
            Exception? resultException = null;
            TryAsync<Token> authorizationAttempt = TryAuthorize(cancellationToken);
            _ = await apply(token => doSomething(token), authorizationAttempt)
                .Match(
                    Succ: async dataTask =>
                    {
                        result = await dataTask;
                    },
                    Fail: exception =>
                    {
                        resultException = exception;
                    }
                )
                .ConfigureAwait(false);

            // FIXME:  Does not wait!!!!

            return result
                ?? ((resultException == null)
                    ? throw new PersonioRequestException("Could not get data from Personio: Reason unknown.")
                    : throw new PersonioRequestException($"Could not get data from Personio: {resultException.Message}", resultException)
                );
        });

如上面的代碼所示,該方法在返回結果之前不會等待,或者碰巧拋出原因未知的異常。

使用調試器,我已經能夠確定authorizationAttempt獲得了一個值並調用了doSomething() ,但是在等待響應時,拋出了錯誤。

使用上述方法的代碼,為doSomething()提供 function 是這樣的:

public TryAsync<RequestResponse<T>> TryGet<T>(RequestOptions options, CancellationToken cancellationToken) =>
        _authorizationClient.WithAuthorization<RequestResponse<T>>(
            async token =>
                {
                    UriBuilder urlBuilder = new(_personioConfig.BaseUrl.AppendPathSegment(options.Endpoint))
                    {
                        // This is used to add parameters which are used for filtering and may be unique for the record type, such as "updated_from".
                        Query = options.QueryParameters.ToString()
                    };

                    RestRequest request = new(urlBuilder.Uri, Method.Get);
                    request.Timeout = -1;
                    if (options.Pagination.IsActive)
                    {
                        request = request.AddQueryParameters(options.Pagination);
                    }

                    request = request
                        .AddHeader("Accept", "application/json")
                        .AddHeader("Authorization", $"Bearer {token.Value}");

                    return await GetRecords<T>(request, cancellationToken);
                },
                cancellationToken
            );

    private async Task<RequestResponse<T>> GetRecords<T>(RestRequest request, CancellationToken cancellationToken)
    {
        RestResponse<RequestResponse<T>> requestResponse = await _restClient.ExecuteAsync<RequestResponse<T>>(request, cancellationToken);

        // FIXME: The next line is never executed.

        RequestResponse<T>? dataResponse = JsonConvert.DeserializeObject<RequestResponse<T>>(requestResponse?.Content ?? "");
        return (requestResponse?.IsSuccessful ?? false)
            ? (dataResponse != null && dataResponse.WasSuccessful)
                ? dataResponse
                : throw new PersonioRequestException("Connected to Personio, but could not get records.")
            : throw (
                (requestResponse?.ErrorException != null)
                    ? new("Could not get records from Personio.", requestResponse.ErrorException)
                    : new($"Could not get records from Personio. {dataResponse?.Error?.Message ?? UnknownProblem}."));
    }

如上面的代碼所示,方法GetRecords()被調用,但系統在ExecuteAsync()有任何結果之前未填充result (返回上面的第一個方法)引發錯誤。

早期形式的代碼,早期版本的 RestSharp(v106) 和同步執行工作正常。 當時, TryGet看起來像:

    public TryAsync<RequestResponse<T>> TryGet<T>(RequestOptions options) =>
        _authorizationClient.WithAuthorization<RequestResponse<T>>(token =>
        {
            UriBuilder urlBuilder = new(_personioConfig.BaseUrl.AppendPathSegment(options.Endpoint))
            {
                // This is used to add parameters which are used for filtering and may be unique for the record type, such as "updated_from".
                Query = options.QueryParameters.ToString()
            };
            _restClient.BaseUrl = urlBuilder.Uri;
            _restClient.Timeout = -1;

            IRestRequest request = new RestRequest(Method.GET);
            if (options.Pagination.IsActive)
            {
                request = request.AddQueryParameters(options.Pagination);
            }

            request = request
                .AddHeader("Accept", "application/json")
                .AddHeader("Authorization", $"Bearer {token.Value}");

            return Task.FromResult(GetRecords<T>(request));
        });

    private RequestResponse<T> GetRecords<T>(IRestRequest request)
    {
        IRestResponse<RequestResponse<T>> requestResponse = _restClient.Execute<RequestResponse<T>>(request);
        RequestResponse<T>? dataResponse = JsonConvert.DeserializeObject<RequestResponse<T>>(requestResponse.Content);
        return requestResponse.IsSuccessful
            ? (dataResponse != null && dataResponse.WasSuccessful)
                ? dataResponse
                : throw new PersonioRequestException("Connected to Personio, but could not get records.")
            : throw (
                (requestResponse.ErrorException != null)
                    ? new("Could not get records from Personio.", requestResponse.ErrorException)
                    : new($"Could not get records from Personio. {dataResponse?.Error?.Message ?? UnknownProblem}."));
    }

我做錯了什么或錯過了什么? 如何使用 RestSharp 107.x 和ExecuteAsync()完成這項工作?

感謝@AlexeyZimarev,我能夠讓 TryGet() 方法工作。

現在看起來像:

    public TryAsync<RequestResponse<T>> TryGet<T>(RequestOptions options, CancellationToken cancellationToken) =>
        TryAsync(async () =>
        {
            _restClient.Authenticator = _authorizationClient;
            Uri uri = new UriBuilder(_personioConfig.BaseUrl.AppendPathSegment(options.Endpoint)).Uri;
            RestRequest request = new RestRequest(uri, Method.Get)
               .AddQueryParameters(options.QueryParameters);

            if (options.Pagination.IsActive)
            {
                request = request.AddQueryParameters(options.Pagination);
            }

            request.Timeout = -1;
            return await GetRecords<T>(request, cancellationToken);
        });

_restClient.Authenticator = _authorizationClient; 這里不是很安全,因為它被分配在注入而不是 RestClient 上,將來可能會在其他地方使用,但我通過使客戶端瞬態而不是 singleton “解決”了這個問題。

我還從PersonioAuthorizationClient中刪除了所有 TryAsync,因為無論AuthenticatorBase正在做什么,它似乎都不能很好地發揮作用。

暫無
暫無

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

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