c# - 任务扩展以迎合应用范围内的服务调用

标签 c# generics xamarin xamarin.forms async-await

我在 xamarin 工作,并尝试使用一种方法使用所有服务。为此,我写了一个 TaskExtension。这样我就可以从应用程序的每个页面调用该扩展方法。这是为了禁用按钮、显示加载屏幕、响应处理以及从一个点满足异常处理。我在下面附上我的代码。需要您对此解决方案的专家意见

这是我的扩展类(class)

public static class TaskExtensions
{
    public static async Task<ResultViewModel<U>> ExecuteAsyncOperation<U>(this Task<HttpResponseMessage> operation, object sender = null)
    {
        ResultViewModel<U> resultModel = new ResultViewModel<U>();
        Button button = BeforeAsyncCall(sender);
        try
        {
            await BackgroundOperation(operation, resultModel);
        }
        catch (Exception ex)
        {
            resultModel.Status = HttpStatusCode.InternalServerError;
            resultModel.Errors = new List<string>() { "Some error occurred. Please try again." };
        }
        finally
        {
            AfterAsyncCall(button);

        }
        return resultModel;
    }
    static async Task BackgroundOperation<U>(Task<HttpResponseMessage> operation, ResultViewModel<U> resultModel)
    {
        HttpResponseMessage RawResult = await operation;
        var Response = await RawResult.Content.ReadAsStringAsync();
        resultModel.Status = RawResult.StatusCode;
        if (RawResult.IsSuccessStatusCode)
        {
            var responseObj = await Task.Run(() => JsonConvert.DeserializeObject<U>(Response));
            resultModel.Result = responseObj;
        }
        else
        {
            var responseErrorObj = await Task.Run(() => JsonConvert.DeserializeObject<ErrorModel>(Response));
            resultModel.Errors = new List<string>();
            foreach (var modelState in responseErrorObj.ModelState)
            {
                foreach (var error in modelState.Value)
                {
                    resultModel.Errors.Add(error.ToString());
                }
            }
        }
    }
    static Button BeforeAsyncCall(object sender)
    {
        Button button = null;
        if (sender != null)
            button = (Button)sender;
        if (button != null)
            button.IsEnabled = false;
        UserDialogs.Instance.ShowLoading("Loading", MaskType.Black);
        return button;
    }
    static void AfterAsyncCall(Button button)
    {
        UserDialogs.Instance.HideLoading();
        if (button != null)
            button.IsEnabled = true;
    }
}

这是对我的扩展方法的调用

ResultViewModel<TokenModel> response = await new Service(Settings.BaseUrl).Login(loginModel).ExecuteAsyncOperation<TokenModel>(sender);

ResultViewModel

public class ResultViewModel<T> 
    {
        public HttpStatusCode Status { get; set; }
        public T Result { get; set; }
        public List<string> Errors { get; set; }
    }

异步方法

public async Task<HttpResponseMessage> Login(LoginViewModel loginModel)
        {
            try
            {

                var dataList = new List<KeyValuePair<string, string>>();
                dataList.Add(new KeyValuePair<string, string>("grant_type", "password"));
                dataList.Add(new KeyValuePair<string, string>("username", loginModel.Email));
                dataList.Add(new KeyValuePair<string, string>("password", loginModel.Password));
                var request = new HttpRequestMessage()
                {
                    RequestUri = new Uri(this.BaseUrl + "token"),
                    Method = HttpMethod.Post,
                    Content = new FormUrlEncodedContent(dataList)
                };
                var authenticateResponse = await Client.SendAsync(request);
                return authenticateResponse;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

我的问题是

1) 这是一个好方法吗?

2) 我们能否在性能方面对其进行改进?

3) 我是否正确使用了 Async?

最佳答案

1) Is this a good approach?

这种方法没有错。

2) Can we improve it in terms of performance?

使用扩展方法应该不会有任何性能问题,但您可以 100% 确定地进行测量。您正在使用 objectbutton 转换创建装箱和拆箱情况。您可以只使用 Button 吗?如果您想支持多种元素类型,请使用 ViewElement。此外,使用 async await 会受到惩罚,但它们是最小的并且是不阻塞 UI 所必需的。您可以通过在任务中添加 .ConfigureAwait(false) 来消除重新捕获上下文的需要,从而提高性能,但在您的情况下,您需要上下文才能重新启用按钮。 dynamic 的使用似乎没有必要,而且确实有一些开销。

3) Am I using Async correctly?

如果该方法仅返回一个 Task,则您不必 await Task。您可以从调用方法中 await 它。这将减少编译器的开销,并可能提高性能。我之前没有测试过这个。

扩展

public static async Task<ResultViewModel<T>> ExecuteAsyncOperation<T>(this Task<HttpResponseMessage> operation, Button button)
{
    ResultViewModel<T> resultModel = new ResultViewModel<T>();
    try
    {
        if (button != null)
            button.IsEnabled = false;
        HttpResponseMessage RawResult = await operation;
        string Response = await RawResult.Content.ReadAsStringAsync();
        resultModel.Status = RawResult.StatusCode;

        if (RawResult.IsSuccessStatusCode)
        {
            var responseObj = JsonConvert.DeserializeObject<T>(Response);
            resultModel.Result = responseObj;
        }
        else
        {
            //create an error model instead of using dynamic I am guessing modelstate here
            List<ModelState> responseObj = JsonConvert.DeserializeObject<List<ModelState>>(Response);
            resultModel.Errors = new List<string>();
            foreach (ModelState modelState in responseObj)
            {
                foreach (var error in modelState.Errors)
                {
                    resultModel.Errors.Add(error.ToString());
                }
            }
        }
    }
    catch (Exception ex)
    {
        resultModel.Status = HttpStatusCode.InternalServerError;
        resultModel.Errors = new List<string>() { "Some error occurred. Please try again." };
    }
    finally
    {
        if (button != null)
            button.IsEnabled = true;
    }
    return resultModel;
}

通话

var button = sender as Button; 
if (button != null)
{
    ResultViewModel<TokenModel> response = await new Service(Settings.BaseUrl).Login(loginModel).ExecuteAsyncOperation<TokenModel>(sender);
}

请求

public Task<HttpResponseMessage> Login(LoginViewModel loginModel)
{
    var dataList = new List<KeyValuePair<string, string>>();
    dataList.Add(new KeyValuePair<string, string>("grant_type", "password"));
    dataList.Add(new KeyValuePair<string, string>("username", loginModel.Email));
    dataList.Add(new KeyValuePair<string, string>("password", loginModel.Password));
    var request = new HttpRequestMessage()
    {
        RequestUri = new Uri(this.BaseUrl + "token"),
        Method = HttpMethod.Post,
        Content = new FormUrlEncodedContent(dataList)
    };
    return Client.SendAsync(request);
}

关于c# - 任务扩展以迎合应用范围内的服务调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50515366/

相关文章:

generics - 如何为泛型类型编写扩展方法,其中一个类型变量必须是字符串?

java - Java 9 或更高版本中的预计泛型特化,vs List<int> : how will . remove() 工作?

C#类型参数规范

xamarin.ios - Apple Mac OS 可以在 Windows 中的虚拟机上作为 Xamarin.iOS 的构建主机运行吗?

c# - VS2008 调试器是否保留超出范围的对象?

c# - ASP.NET 图表控件的默认 3D 图表透明度?

c# - 虚拟方法/属性的分支

c# - 是 mongodb atomic 中的 upsert 过滤器和实际更新

cocoa - 分配 ContentView 后如何调整 NSWindow 的大小?

c# - 如何从代码动态填充ListPreference?