首页 > 解决方案 > 如何正确调用 Parallel.ForEach 循环中的调用异步方法

问题描述

如何正确调用 Parallel.ForEach 循环中的调用异步方法。billDataService.SaveBillDetail 和 GetProfileDetails 都是异步方法。在 SaveBillDetail 中保存在 MongoDB 中的数据。

public async Task ConvertXMLFileToJSON()
        {
            try
            {

                if (Directory.Exists(_appSettings.DocumentsStorage))
                {
                    int i = 1; //Test
                    bool exists = Directory.GetFiles(_appSettings.DocumentsStorage).Any(x => x.Equals(Path.Combine(_appSettings.DocumentsStorage, "Ready.txt"), StringComparison.OrdinalIgnoreCase)); //Need to check
                    if (exists)
                    {
                      Parallel.ForEach(System.IO.Directory.GetFiles(_appSettings.DocumentsStorage, "*.xml"), (currentFile) =>
                        {
                            try
                            {
                                XElement root = XElement.Load(currentFile); // or .Parse(string);
                                //Removing CDATA property from XElement.
                                XElement items = XElement.Parse(root.ToString().Replace("<![CDATA", "").Replace("]]>", "").Replace("[", ""));
                                //Removing XML_INFO Tag from XElement.
                                items.Elements("XML_INFO").Remove();
                                XmlDocument xmlDoc = new XmlDocument();
                                using (XmlReader xmlReader = items.CreateReader())
                                {
                                    xmlDoc.Load(xmlReader);
                                }

                                var json = JsonConvert.SerializeXmlNode(xmlDoc);
                                billDetails obj = JsonConvert.DeserializeObject<billDetails>(json);
                                BillDetails billDetails = new BillDetails();
                                billDetails.AccountNumber = obj.BILL_INFO.BILL_RUN.ACCT_INFO.ACCT_CODE;
                                billDetails.MobileNumber = obj.BILL_INFO.BILL_RUN.ACCT_INFO.PRINCIPAL_NO.STR_PRINCIPAL_NO;
                                billDetails.BillDate = DateTime.ParseExact(obj.BILL_INFO.BILL_RUN.BILL_PROP.TO_DATE, "dd/MM/yyyy", CultureInfo.InvariantCulture);
                                billDetails.DueAmount = obj.BILL_INFO.BILL_RUN.ACCT_INFO.ACCT_BALANCE_TRACE.TOTAL_DUE;
                                billDetails.CustomerName = obj.BILL_INFO.BILL_RUN.CUST_INFO.CUST_NAME.FULL_NAME;
                                billDetails.InvoiceId = obj.BILL_INFO.BILL_RUN.BILL_PROP.INVOICE_ID;
                                billDetails.DueDate = DateTime.ParseExact(obj.BILL_INFO.BILL_RUN.BILL_PROP.DUE_DATE, "yyyyMMdd hh:mm:ss", CultureInfo.InvariantCulture);
                                billDetails.RepositoryName = "postpaid";
                                billDetails.BillRun = obj.BILL_INFO.BILL_RUN; //tempObj2.BILL_INFO.ToString().Remove(0, 1);
                                billDetails.ObjectId = Guid.NewGuid().ToString();
                                if (billDetails != null)
                                {
                                    BillDataService billDataService = new BillDataService(_dbConfig);
                                    Console.WriteLine("SaveBillDetail");
                                    if (billDataService.SaveBillDetail(billDetails) != null)
                                    {
                                        Console.WriteLine("SaveBillDetail done");
                                        GetProfileDetails(billDetails);
                                        _logger?.LogInformation(i++ + "  File Success");
                                        Console.WriteLine(i++ + "  File Success");
                                        // File.Delete(file);  //Delete File 
                                    }
                                }

                            }
                            catch (Exception ex)
                            {
                                _logger?.LogError(ex, "Error");
                            }
                            finally { }
                        });
                    }
                }
            }

            catch (Exception ex)
            {
                _logger?.LogError(ex, "Error");
            }
            finally
            {

            }
        }


public async Task GetProfileDetails(BillDetails billDetails)
        {
            try
            {
                ProfileService profileService = new ProfileService(_dbConfig);
                var searchFilter = new SearchFilter
                {
                    Filters = new List<Filter>()
                };

                if (!string.IsNullOrEmpty(billDetails.AccountNumber))
                {
                    searchFilter.Filters.Add(new Filter() { PropertyName = "AccountNumber", Operator = Operator.Equals, Value = billDetails.AccountNumber, CaseSensitive = true });
                }

                if (searchFilter != null)
                {
                    Profile profile = await profileService.GetProfiles(searchFilter);
                    if (profile != null)
                    {
                        await SendMailNotification(profile, billDetails);
                    }
                    else
                    {
                        _logger?.LogError("Profile Info not found");

                    }
                }
            }
            catch (Exception ex)
            {
                _logger?.LogError(ex, "Error");
                throw;
            }
            finally { }
        }


正常的每个循环我可以在 MongoDB 中调用和保存数据。但是 Parallel.ForEach 循环我无法使用 await 调用异步方法,并且在 mongo 中保存数据也不起作用。在 Parallel.ForEach 循环内部我避免使用 await 的调用方法的前面。

标签: c#.netmongodbasp.net-core

解决方案


您可以将代码更改为如下所示,然后等待所有任务完成。

var fileTasks = System.IO.Directory.GetFiles(_appSettings.DocumentsStorage, "*.xml").Select(async currentFile =>
                        {
                            try
                            {
                                XElement root = XElement.Load(currentFile); // or .Parse(string);
                                // rest o your code here
                                if (billDetails != null)
                                {
                                    if (billDataService.SaveBillDetail(billDetails) != null)
                                    {
                                        Console.WriteLine("SaveBillDetail done");
                                        await GetProfileDetails(billDetails);
                                    }
                                }
                             }
                          catch(exception ex) { //log exeption }
                       });

await Task.WhenAll(fileTasks);

推荐阅读