There's a few questions that address the async controller workings but none quite deal with what I needed. hopefully someone's already done this and can tell me if I'm doing something wrong.
I've got a combination of a web application that deals with configuring tasks and a console application counterpart that deals with running configured tasks. I wanted to be able to run the tasks from web application by clicking a button on which control is immediately returned to the user and the task is executed in the background. AsyncController seems like a perfect match. Both applications access the same database using EF6, Unity dependency injection and SQL Server 2012. Web interface is targeting .NET 4.5 on MVC4.
I can run the console application perfectly fine without a hitch. I can also trigger the run from the web interface using below code. The only problem is when triggered through web application, the task will run to a point (I can see it in the logs (Nlog)) but it stops executing until I rebuild the solution - solution contains both applications, which would also replace the .exe being run. I don't get any pauses when I run the console application directly.
It's my first time using Task and I'm a bit shy with the Process class too. Please don't be too harsh if I'm doing something incredibly stupid.
Here's the code for the controller:
public class TaskRunnerController : AsyncController
{
private readonly ITaskProcessService _taskProcessService;
private readonly IAuthenticationContext _context;
private readonly IServiceBase<Task> _taskService;
public TaskRunnerController(ITaskProcessService taskProcessService,
IAuthenticationContext context,
IServiceBase<Task> taskService)
{
_taskProcessService = taskProcessService;
_context = context;
_taskService = taskService;
}
private string TaskRunnerExe
{
get
{
var setting = ConfigurationManager.AppSettings["TaskRunner.Exe"];
Guard.Against<ConfigurationErrorsException>(string.IsNullOrEmpty(setting), "Missing configuration setting: TaskRunner.Exe");
return setting;
}
}
[CustomAuthorize(typeof(CanRun))]
public ActionResult RunTaskAsync(long id)
{
var task = _taskService.Find(i => i.TaskId == id);
Guard.AgainstLoad(task, id);
var fileInfo = new FileInfo(TaskRunnerExe);
Guard.Against<ConfigurationErrorsException>(!fileInfo.Exists, "TaskRunner could not be found at specified location: {0}", TaskRunnerExe);
var taskProcess = _taskProcessService.Schedule(task, _context.Principal.Identifier);
AsyncManager.OutstandingOperations.Increment();
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
ProcessStartInfo info = new ProcessStartInfo(fileInfo.FullName,
string.Format("{0} {1}", task.TaskId, taskProcess.TaskProcessId));
info.UseShellExecute = false;
info.RedirectStandardInput = true;
info.RedirectStandardOutput = true;
info.CreateNoWindow = true;
var process = new Process { StartInfo = info, EnableRaisingEvents = true };
process.Exited += (sender, e) =>
{
AsyncManager.OutstandingOperations.Decrement();
};
process.Start();
});
if (_context.Principal.HasPermission<CanViewList>())
return RedirectToAction("Index", "Tasks");
return RedirectToAction("Index", "Home");
}
public ActionResult RunTaskProgress()
{
return View();
}
public ActionResult RunTaskCompleted()
{
return Content("completed", "text/plain");
}
}
Dependencies:
- taskProcessService: repository for each event of task runs
- context: keeps information about the current user
- taskService: repository for tasks
The console application (.exe) exits when it's completely finished. As I mentioned, when invoked through web application, the task will only finish when I rebuild the application - at this point it does everything it should - it seems like it was working the whole time, it just stopped logging or reporting back at some point in the process.
Perhaps it's important - I had another try at this without the Async controller, but with the same setup - run the Process, wrapped in a task which had the same end effect. I thought the process was being killed when the main thread was returned to the pool, that's why I tried the AsyncController.
If I open task manager I can see the process for the exe sits there idle. The application logs it's progress up to a point but then just sits there. Is this a problem with VS? I'm using VS2012.
Am I wrong to wrap the Process into a Task? Is there a way to run a executable directly via a Task class and the Action class?
Many thanks for any insight.