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.
More than likely, the application is being terminated because the worker process ends. IIS will terminate any threads or child processes running under a requests thread after it has finished.
As such, it's simply not viable to do what you're doing. You could move your console app to a schedule task, and then trigger your scheduled task from web application.