【问题标题】:WPF SimpleInjector call to client.GetAsync hanging - (We think it is because the Task not starting/returning)WPF SimpleInjector 调用 client.GetAsync 挂起 -(我们认为这是因为任务没有启动/返回)
【发布时间】:2022-11-24 05:35:48
【问题描述】:

我正在尝试在 WPF 应用程序(.NET 框架)中使用 SimpleInjector。我们在许多服务中以完全相同的方式使用它,但出于某种原因,当我尝试在此 WPF 应用程序中实现相同的逻辑时,对 HttpClient().GetAsync 的调用挂起。我们认为这是因为由于某种原因任务没有执行。

我正在从 App.xaml.cs 的 OnStartUp 元素注册对象,如下所示。在 SetupService 构造函数中,我们调用一个 SetupService url(在 App.Config 的 SetupConfiguration 部分中设置)以获取要在应用程序中使用的 SetupResponse。

它最终挂在 Service Client.GetAsync 方法中,我试图展示下面的流程:

所有类似乎都已正确注入,并且 ServiceClient 的填充方式与我们的一项工作服务中的相同点完全相同。我们不知道发生了什么,也不知道如何解决这个问题。

最后,SetupService 被注入到其他类中 - 所以我宁愿让它像这样工作,而不是从 SimpleInjector 机制中删除调用。

很感谢任何形式的帮助,

谢谢你

安德鲁


public partial class App : Application
    {
        private static readonly Container _container = new Container();

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            RegisterDependencies();
            _container.Verify();

        }

        private void RegisterDependencies()
        {
            var serviceConfigSection = ServiceConfigurationSection.Get();
            
            _container.RegisterSingle<ILoggingProvider, LoggingProvider>();
            _container.RegisterSingle<IServiceClient>(() => new ServiceClient(_container.GetInstance<ILoggingProvider>()));
            _container.RegisterSingle<IConfigurationSection>(() => SetupConfigurationSection.Get());
            _container.RegisterSingle<ISetupService, SetupService>();

       }
}
   public class SetupService: ISetupService
    {
        private static readonly Dictionary<string, string> AcceptType = new Dictionary<string, string>
        {
            {"Accept", "application/xml"}
        };
        private const string AuthenticationType = "Basic";

        private readonly IServiceClient _serviceClient;
        private readonly ILoggingProvider _logger;
        private readonly IConfigurationSection _configuration;


        public SetupService(IConfigurationSection configuration, IServiceClient serviceClient, ILoggingProvider logger)
        {
            _serviceClient = serviceClient;
            _logger = logger;
            _configuration = kmsConfiguration;

            RefreshSetup();
        }

        public void RefreshSetup()
        {
            try
            {
                var token = BuildIdentityToken();

                var authHeaderClear = string.Format("IDENTITY_TOKEN:{0}", token);

                var authenticationHeaderValue =
                    new AuthenticationHeaderValue(AuthenticationType, Convert.ToBase64String(Encoding.ASCII.GetBytes(authHeaderClear)));

                _serviceClient.Url = _configuration.Url;
                var httpResponse = _serviceClient.GetAsync(string.Empty, authenticationHeaderValue, AcceptType).Result;

                var responseString = httpResponse.Content.ReadAsStringAsync().Result;

                _response = responseString.FromXML<SetupResponse>();
            }
            catch (Exception e)
            {
                throw
            }
        }
    public class ServiceClient : IServiceClient
    {
        private const string ContentType = "application/json";
        private string _userAgent;

        private ILoggingProvider _logger;

        public string Url { get; set; }
        public string ProxyAddress { get; set; }
        public int TimeoutForRequestAndResponseMs { get; set; }
        public int HttpCode { get; private set; }

        public ServiceClient(ILoggingProvider logger = null)
        {
            _logger = logger;
        }


        public async Task<HttpResponseMessage> GetAsync(string endpoint, AuthenticationHeaderValue authenticationHeaderValue = null, IDictionary<string, string> additionalData = null, IDictionary<string, string> additionalParams = null)
        {
            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri(Url);
                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(ContentType));
                if (authenticationHeaderValue != null)
                    client.DefaultRequestHeaders.Authorization = authenticationHeaderValue;
                ProcessHeader(client.DefaultRequestHeaders, additionalData);
                var paramsQueryString = ProcessParams(additionalParams);
                if (!string.IsNullOrEmpty(paramsQueryString))
                    endpoint = $"{endpoint}?{paramsQueryString}";
                
                return await client.GetAsync(endpoint); **// HANGS ON THIS LINE!**

            }
        }
    }
}

【问题讨论】:

  • 为什么你认为SimpleInjector与它有什么关系?为什么你认为这是因为任务没有开始?很可能是因为您使用 .Result 调用阻塞了 UI 线程。

标签: c# wpf async-await simple-injector


【解决方案1】:

如果您阻塞来自 UI 线程的异步代码,那么您可能会遇到死锁。我对此进行了完整的解释on my blog。在这种情况下,死锁的原因是Result。有几个解决方案。

我推荐的方法是重新考虑您的用户体验。您的 UI 不应在显示任何内容之前阻塞 HTTP 调用以完成;相反,立即(并同步)显示一个 UI(即一些“正在加载...”屏幕),然后更新HTTP 调用完成时的那个 UI。

另一种是在启动时阻塞。为此有一个few patterns。它们都不是在所有情况下都有效,但是通常工作原理是将异步工作包装在 Task.Run 中,然后阻塞它,例如 var httpResponse = Task.Run(() =&gt; _serviceClient.GetAsync(string.Empty, authenticationHeaderValue, AcceptType)).GetAwaiter().GetResult(); 和类似的其他阻塞调用。

在显示 UI 之前阻塞通常被认为是糟糕的 UX。应用商店通常不允许它。所以这就是我建议更改 UX 的原因。您可能会发现 an approach like this 有帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多