AsyncController用于运行控制台应用程序

时间:2014-10-15 16:45:08

标签: asp.net-mvc-4 process console-application executable asynccontroller

有一些问题可以解决异步控制器的工作问题,但没有一个问题可以解决我需要的问题。希望有人已经这样做,可以告诉我,如果我做错了什么。

我有一个处理配置任务的Web应用程序和一个处理已运行配置任务的控制台应用程序对应的组合。我希望能够通过单击立即将控件返回给用户的按钮从Web应用程序运行任务,并在后台执行任务。 AsyncController似乎是一个完美的匹配。两个应用程序都使用EF6,Unity依赖注入和SQL Server 2012访问同一个数据库.Web接口的目标是在MVC4上使用.NET 4.5。

我可以完美地运行控制台应用程序,没有任何障碍。我也可以使用下面的代码从Web界面触发运行。唯一的问题是当通过Web应用程序触发时,任务将运行到某一点(我可以在日志中看到它(Nlog))但它会停止执行,直到我重建解决方案 - 解决方案包含两个应用程序,这也将替换。 exe正在运行。当我直接运行控制台应用程序时,我没有任何暂停。

这是我第一次使用Task而且我对Process类有点害羞。如果我做一些非常愚蠢的事情,请不要太苛刻。

这是控制器的代码:

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");
    }
}

依赖关系:

  • taskProcessService:每个任务运行事件的存储库
  • context:保留有关当前用户的信息
  • taskService:任务存储库

控制台应用程序(.exe)在完成后退出。正如我所提到的,当通过Web应用程序调用时,任务只会在我重建应用程序时完成 - 此时它会完成应有的一切 - 似乎它一直在工作,它只是在某个时候停止记录或报告在这个过程中。

也许这很重要 - 我在没有Async控制器的情况下再试一次,但是使用相同的设置 - 运行Process,包含在具有相同结束效果的任务中。我认为当主线程返回到池时,进程被杀死了,这就是我尝试使用AsyncController的原因。

如果我打开任务管理器,我可以看到exe的进程闲置。应用程序将它的进度记录到某一点,但随后只是坐在那里。这是VS的问题吗?我正在使用VS2012。

将进程包装成任务是错误的吗?有没有办法直接通过Task类和Action类运行可执行文件?

非常感谢任何见解。

2 个答案:

答案 0 :(得分:0)

很可能,应用程序正在终止,因为工作进程结束。 IIS将在请求线程完成后终止在请求线程下运行的所有线程或子进程。

因此,做你正在做的事情根本不可行。您可以将控制台应用程序移动到计划任务,然后从Web应用程序触发计划任务。

答案 1 :(得分:0)

对此进行更新。我现在已经确定,由进程启动的exe(控制台应用程序)确实在后台运行正常。但是由于某种原因,当它因某种原因被重定向时,它会停止写入标准输出流。我已经更新了正在运行的控制台应用程序,这样它就可以直接将日志添加到数据库中的任务中,并且运行正常。我可以实时跟踪网站上的任务进度。

我还验证了没有内存泄漏(控制台应用程序和网站都是托管代码)。一旦exe完成,它将按照我的预期从进程列表中删除。

重点是,上面的代码有效,但我仍然不确定为什么标准输出没有被写入。可能是因为在控制台应用程序中,我使用依赖注入的Nlog通过ConsoleTarget写入Console。