首页 > 解决方案 > 异步 /Await 调用卡在 Web API 中

问题描述

我们有一个在 .net4.6.1 上运行的 Web API 应用程序。我们已经多次尝试找出导致死锁的根本原因,但都失败了。下面是代码片段。我们每 1 分钟访问一次这个 API 端点。它将一次从数据库中选择 300 个事务进行处理。我们观察到,当数据库中没有要处理的文件时,它会卡住。不过不确定。如果有人可以帮助我们,那将会很有帮助。TIA

 public class TaxEngineIntegratorController : ApiController
  {

    public async Task Get(int id)
    {

        try
        {

            await MainFileMethod();
           

        }
        catch (Exception Ex)
        {
            SerilogMethods.LogError(log, Ex, "Get");

        }

    }

  public async Task MainFileMethod()
    {

        List<FileTransaction> lstFTtoLock = new List<FileTransaction>();
        try
        {
            List<int> lstStatusIds = new List<int>();
            lstStatusIds.Add(objStatusManager.GetStatusIdbyName(Status.ConversionToXmlSucceded));
            lstStatusIds.Add(objStatusManager.GetStatusIdbyName(Status.Reprocess));
            //Getting the serviceURL of TRTaxEngine
            string seriviceURL = objConfigManager.GetConfigurationdbyKey(ConfigurationList.TRTaxEngineURL);
            //Getting the output path for the file to be placed after processing
            string outputfilePath = objConfigManager.GetConfigurationdbyKey(ConfigurationList.TRTaxOutputXMLFolder);
            FileMasterManager objFileMasterManager = new FileMasterManager();
            TRTaxXMLOperations objxmlresp = new TRTaxXMLOperations();

            //Getting all the files list for proccessing from the DB
            List<FileTransaction> lstFiletoProcess = await objTransManager.GetFileListforProcessingAsync(lstStatusIds, true);
            lstFTtoLock = lstFiletoProcess;

            

            if (lstFiletoProcess.Count == 0)
                return;
                


                if (lstFiletoProcess.Count > 0)
                {
                var tasks = new List<Task<string>>();
                using (HttpClient httpClnt = new HttpClient())
                {
                    httpClnt.Timeout = TimeSpan.FromMilliseconds(-1);
                    

                    foreach (FileTransaction item in lstFiletoProcess)
                    {


                        TRXMLResponseModel objRespModel = new TRXMLResponseModel();
                        objRespModel.strxmlResponse = string.Empty;

                        string fullFileName = item.FilePath + item.ConvertedName;
                        objRespModel.outputFilename = outputfilePath + item.ConvertedName;

                        FileMaster fileMaster = objFileMasterManager.GetById(item.FileId);
                        //Proccessing the file and getting the output filedata                        
                        Task<string> t = objxmlresp.GetXMLResponse(seriviceURL, fullFileName, fileMaster.CountryId.GetValueOrDefault(), httpClnt, objFileOperation, objRespModel.outputFilename, item);
                        tasks.Add(t);
                        objRespModel.strxmlResponse = await t;
                        
                    }
                    var result = await Task.WhenAll(tasks);

                }
               
                SerilogMethods.LogCustomException(log, "Http Client Destroyed in Tax Engine", "GetXMLResponse");
            }

        }
        catch (Exception Ex)
        {
            if (lstFTtoLock != null && lstFTtoLock.Count > 0)
            {
                objTransManager.UpdateFileTransactionIsPickedtoFalse(lstFTtoLock);
            }
            throw Ex;

        }

    }

}

//从数据库中获取所有要处理的文件列表

    public async Task<List<FileTransaction>> GetFileListforProcessingAsync(List<int> lstStatusList, bool IsActive)
    {
        try
        {
            List<FileTransaction> lstFTList = new List<FileTransaction>();

           
            using (SUTBACDEVContext db = new SUTBACDEVContext())
            {
                //DataTable dtFileTransactions = GetFileTransactionListAsync(lstStatusList, IsActive);
                string connectionString = db.Database.GetDbConnection().ConnectionString;
                var conn = new SqlConnection(connectionString);

                string query = @"[SUTGITA].[GetFileListforProcessing]";
                using (var sqlAdpt = new SqlDataAdapter(query, conn))
                {
                    sqlAdpt.SelectCommand.CommandType = CommandType.StoredProcedure;
                    sqlAdpt.SelectCommand.Parameters.AddWithValue("@StatusId", string.Join(",", lstStatusList.Select(n => n.ToString()).ToArray()));
                    sqlAdpt.SelectCommand.Parameters.AddWithValue("@IsActive", IsActive);
                    sqlAdpt.SelectCommand.CommandTimeout = 60000;
                    DataTable dtFileTransactions = new DataTable();
                    sqlAdpt.Fill(dtFileTransactions);


                    if (dtFileTransactions != null && dtFileTransactions.Rows.Count > 0)
                    {
                        IEnumerable<long> ids = dtFileTransactions.AsEnumerable().ToList().Select(p => p["id"]).ToList().OfType<long>();

                        lstFTList = await db.FileTransaction.Include(x => x.File.Country).Where(x => ids.Contains(x.Id)).OrderBy(x => x.Id).ToListAsync();
                    }                        
                }
                
            }
            return lstFTList;
        }
        catch (Exception ex)
        {

            throw ex;
        }

    }


public async Task<string> GetXMLResponse(string baseUrl, string fullFileName, int countryId, HttpClient client, FileOperations objFileOperation, string outputfilePath, FileTransaction item)
    {

        try
        {
            var fileData = new StringBuilder(objFileOperation.ReadFile(fullFileName));
            using (HttpContent content = new StringContent(TransformToSOAPXml(fileData, countryId), Encoding.UTF8, "text/xml"))
            {
                using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, baseUrl))
                {
                    request.Headers.Add("SOAPAction", "");
                    request.Content = content;
                    using (HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead))
                    {
                        response.EnsureSuccessStatusCode();
                        if (response.IsSuccessStatusCode)
                        {
                            using (Stream streamToReadFrom = await response.Content.ReadAsStreamAsync())
                            {
                                using (Stream streamToWriteTo = File.Open(outputfilePath, FileMode.Create))
                                {
                                    await streamToReadFrom.CopyToAsync(streamToWriteTo);
                                }
                            }
                            var transactionEntry = new FileTransaction
                            {
                                FileId = item.FileId,
                                FilePath = outputfilePath,
                                ConvertedName = item.ConvertedName,
                                ActionedBy = Process.Process3,
                                TimeStamp = DateTime.UtcNow,
                                StatusId = objStatusManager.GetStatusIdbyName(Status.OutputXmlReceived),
                                IsActive = true,
                                CreatedBy = Others.Scheduler,
                                CreatedOn = DateTime.UtcNow,
                                ModifiedBy = Others.Scheduler,
                                ModifiedOn = DateTime.UtcNow
                            };

                            //Inserting the new record and Updating isActive filed of previous record in Tranasaction table(Calling updateDataonTRSuccess method of TRTaxXMLOperations class)
                            await updateDataonTRSuccessAsync(item, transactionEntry);
                            return "Success";
                        }
                        else
                        {
                            SerilogMethods.LogCustomException(log, "Error occured in Tax Engine", "GetXMLResponse");
                            //Log the SOAP response when the SOAP fails with an error message
                            if (response.Content != null)
                            {
                                throw new Exception(await response.Content.ReadAsStringAsync());
                            }

                            return null;

                        }

                    }
                }
            }

        }
        catch (Exception ex)
        {
            SerilogMethods.LogError(log, ex, "GetXMLResponse");
            return null;

        }
    }

我进行了以下更改以使其适用于此特定方法。删除这一行:objRespModel.strxmlResponse = await t; 并将 configureawait(false) 添加到此行:List lstFiletoProcess = await objTransManager.GetFileListforProcessingAsync(lstStatusIds, true).ConfigureAwait(false); 下面是工作代码

   public async Task MainFileMethod()
    {

        List<FileTransaction> lstFTtoLock = new List<FileTransaction>();
        try
        {
            List<int> lstStatusIds = new List<int>();
            lstStatusIds.Add(objStatusManager.GetStatusIdbyName(Status.ConversionToXmlSucceded));
            lstStatusIds.Add(objStatusManager.GetStatusIdbyName(Status.Reprocess));
            //Getting the serviceURL of TRTaxEngine
            string seriviceURL = objConfigManager.GetConfigurationdbyKey(ConfigurationList.TRTaxEngineURL);
            //Getting the output path for the file to be placed after processing
            string outputfilePath = objConfigManager.GetConfigurationdbyKey(ConfigurationList.TRTaxOutputXMLFolder);
            FileMasterManager objFileMasterManager = new FileMasterManager();
            TRTaxXMLOperations objxmlresp = new TRTaxXMLOperations();

            //Getting all the files list for proccessing from the DB
            List<FileTransaction> lstFiletoProcess = await objTransManager.GetFileListforProcessingAsync(lstStatusIds, true).ConfigureAwait(false);
            lstFTtoLock = lstFiletoProcess;

   
            if (lstFiletoProcess.Count == 0)
                return;
          

            if (lstFiletoProcess.Count > 0)
            {
                var tasks = new List<Task<string>>();
                using (HttpClient httpClnt = new HttpClient())
                {
                    httpClnt.Timeout = TimeSpan.FromMilliseconds(-1);
                    //Getting the files for processing 
                  
                    foreach (FileTransaction item in lstFiletoProcess)
                    {
                        TRXMLResponseModel objRespModel = new TRXMLResponseModel();
                        objRespModel.strxmlResponse = string.Empty;

                        string fullFileName = item.FilePath + item.ConvertedName;
                        objRespModel.outputFilename = outputfilePath + item.ConvertedName;

                        FileMaster fileMaster = objFileMasterManager.GetById(item.FileId);
                        //Proccessing the file and getting the output filedata                        
                        Task<string> t = objxmlresp.GetXMLResponse(seriviceURL, fullFileName, fileMaster.CountryId.GetValueOrDefault(), httpClnt, objFileOperation, objRespModel.outputFilename, item, objTransManager);
                        tasks.Add(t);

                        //objRespModel.strxmlResponse = await t;
                       
                    }
                    var result = await Task.WhenAll(tasks);

                }                 
            }

        }
        catch (Exception Ex)
        {
            if (lstFTtoLock != null && lstFTtoLock.Count > 0)
            {
                objTransManager.UpdateFileTransactionIsPickedtoFalse(lstFTtoLock);
            }
            throw Ex;

        }

    }

标签: c#asp.netasync-awaitentity-framework-corehttpclient

解决方案


我的建议:

“Get(int id)”方法有点令人困惑。首先,它需要“id”并且什么都不做。它也不返回任何内容,因此它不是“获取”方法。它基本上是要求状态为“Status.ConversionToXmlSucceded”和“Status.Reprocess”的所有事务,并且可以通过“objxmlresp.GetXMLResponse”方法获取和处理......你不必等待“MainFileMethod(); " 在 "Get(int id)" 中只返回任务或返回 Ok(); 并允许所有过程在后台进行。您可以尝试减少“sqlAdpt.SelectCommand.CommandTimeout = 60000;”。


推荐阅读