0

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

我有一个处理配置任务的 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:任务运行的每个事件的存储库
  • 上下文:保存有关当前用户的信息
  • taskService:任务的存储库

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

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

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

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

非常感谢您的任何见解。

4

2 回答 2

0

由于工作进程结束,应用程序很可能被终止。IIS 将在完成后终止在请求线程下运行的任何线程或子进程。

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

于 2014-10-15T20:20:00.520 回答
0

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

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

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

于 2014-10-16T12:31:13.687 回答