在我的Web Api应用程序中,我需要执行以下主题:Best way to run a background task in ASP.Net web app and also get feedback?在应用程序中,用户可以上载excel文件,然后将其数据导入数据库中的表。一切正常,但导入过程可能需要很长时间(大约20分钟,如果excel有很多行)并且当进程启动时页面被阻止,用户必须等待所有这些时间。我需要在后台运行此导入过程。
我有一个控制器使用这个POST方法:
[HttpPost]
public async Task<IHttpActionResult> ImportFile(int procId, int fileId)
{
string importErrorMessage = String.Empty;
// Get excel file and read it
string path = FilePath(fileId);
DataTable table = GetTable(path);
// Add record for start process in table Logs
using (var transaction = db.Database.BeginTransaction()))
{
try
{
db.uspAddLog(fileId, "Process Started", "The process is started");
transaction.Commit();
}
catch (Exception e)
{
transaction.Rollback();
importErrorMessage = e.Message;
return BadRequest(importErrorMessage);
}
}
//Using myHelper start a store procedure, which import data from excel file
//The procedure also add record in table Logs when it is finished
using (myHelper helper = new myHelper())
helper.StartImport(procId, fileId, table, ref importErrorMessage);
if (!String.IsNullOrEmpty(importErrorMessage))
return BadRequest(importErrorMessage);
return Ok(true);
}
我还有一个GET方法,它返回有关文件及其进程的信息
[HttpGet]
[ResponseType(typeof(FileDTO))]
public IQueryable<FileDTO> GetFiles(int procId)
{
return db.LeadProcessControl.Where(a => a.ProcessID == procId)
.Project().To<FileDTO>();
}
它返回JSON,如下所示:
{
FileName = name.xlsx
FileID = 23
ProcessID = 11
Status = Started
}
此方法适用于GRID
File name | Status | Button to run import | Button to delete file
此Status
来自表格Logs
,FileDTO
表示最后一个值,例如,如果我上传文件status
将是“文件已上传”运行导入status
将为“已启动”,完成后status
将为“完成”。但是现在导入过程运行时页面被锁定,因此状态始终为“完成”。
所以我需要在后台运行程序,如果GET方法已被更改,则应该返回新的Status
。有什么建议吗?
答案 0 :(得分:1)
向方法添加异步不会使方法调用异步。它只是表明处理当前请求的线程可以在等待某些网络/磁盘IO时重用于处理其他请求。当客户端调用此方法时,只有在方法完成后才会获得响应。换句话说,async是完全严重的一面,与客户端调用无关。您需要在单独的线程中启动长时间运行的进程,如下所示。但最佳做法是不使用Web应用程序进行如此长时间运行的处理,而是在单独的Windows服务中进行长时间处理。
[HttpPost]
public async Task<IHttpActionResult> ImportFile(int procId, int fileId)
{
string importErrorMessage = String.Empty;
// Get excel file and read it
string path = FilePath(fileId);
DataTable table = GetTable(path);
// Add record for start process in table Logs
using (var transaction = db.Database.BeginTransaction()))
{
try
{
db.uspAddLog(fileId, "Process Started", "The process is started");
transaction.Commit();
}
catch (Exception e)
{
transaction.Rollback();
importErrorMessage = e.Message;
return BadRequest(importErrorMessage);
}
}
//Start long running process in new thread
Task.Factory.StartNew(()=>{
using (myHelper helper = new myHelper())
{
helper.StartImport(procId, fileId, table, ref importErrorMessage);
//** As this code is running background thread you cannot return anything here. You just need to store status in database.
//if (!String.IsNullOrEmpty(importErrorMessage))
//return BadRequest(importErrorMessage);
}
});
//You always return ok to indicate background process started
return Ok(true);
}