Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to store the active status of a Task, and maintain permanency of a List of these Tasks

I am trying to accurately learn the status of Tasks that are kicked off by a QueueBackgroundWorkerItem thread. I can access the Task object and add them to a List of my TaskModels, and send that list object to my View.

My view only ever shows one one Task status, no matter how many times I click the QueueWorkItem link, and start a new task. I'd like to figure out a couple of things:

  • How, in MVC, do I keep a live List of how many tasks I generated? I assumed by sending the model to the view I would assure some permanency.
  • Once I can do that, I assume I would be able to store the Task objects in my List. However, even in this example below, I still don't seem to be able to know what the Status of the Task is at any given time (it seems I can only know what it is at the time of adding it to my list).

I am hoping someone has done something similar and can help with this. Thanks! -Jason

EDIT: The core requirements of this setup are:

  • I need to use a QueueBackgroundWorkerItem to run a long job even if the browser gets closed
  • I chose to embed a Task so I could learn the ongoing status of each job run. I understand that with the QBWI, that running a task would be overkill. But I could find no other way to know what the status of the QBWI is at any time.

CONTROLLER:

List<TaskModel> taskModelList = new List<TaskModel>();

public ActionResult QueueWorkItem()
{
    Task task;
    ViewBag.Message = "State: ";
    String printPath = @"C:\Work\QueueBackgroundWorkerItemPractice\QueueBackgroundWorkerItemPractice\WorkerPrintFile" + DateTime.Now.ToLongTimeString().ToString().Replace(":", "_") + ".txt";
    System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(cancellationToken =>
    {
        task = Task.Run(() =>
        {
            string filePath = printPath;
            string text = "File line ";
            if (!System.IO.File.Exists(filePath))
            {
                using (var stream = System.IO.File.Create(filePath)) { }
            }
            TextWriter tw = new StreamWriter(printPath);

            for (int i = 0; i < 400; i++)
            {
                text = "Line " + i;

                tw.WriteLine(text);

                Thread.Sleep(200);
            }

            tw.Close();
        });

        var c = task.ContinueWith((antecedent) =>
        {

            taskModelList.Add(new TaskModel(task));

        });

    });

    return View(taskModelList);
}

VIEW:

@model List<QueueBackgroundWorkerItemPractice.Models.TaskModel>

@{
    ViewBag.Title = "Queue Background Worker";
}
<h2>@ViewBag.Title.</h2>
<h3>@ViewBag.Message<span id="modelClass"></span></h3>

<p>Use this area to provide additional information.</p>

@{ 
    <ul>
        @foreach (var taskModel in Model)
        {
            <li>@taskModel.Status</li>
        }
    </ul>
}

EDIT, solution:

Following Raffaeu's advice, and the following compromises, I was able to find it as such:

  • We are only running one task at any given time. I don't need a list
  • I don't truly need the status on-demand. I only need to know when it's completed

I wanted to be able to leverage the Task ID to instantiate the task later from the ID. That proved to involve more overhead than necessary.

Instead I found the feature Task.CompletedTask (available in .NET 4.6 and up). This, used in async, allowed me to get the status of the Task when it is complete. Voila. Thanks to everyone for your suggestions.

The best part - this long-running task will complete whether I close the browser...or stop IIS. Miraculous.

public ActionResult QueueWorkItem()
{
    System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(cancellationToken =>
    {
        task = Task.Run(async () =>
        {
            String printPath = @"C:...";
            string filePath = printPath;
            string text = "File line ";
            if (!System.IO.File.Exists(filePath))
            {
                using (var stream = System.IO.File.Create(filePath)) { }
            }
            TextWriter tw = new StreamWriter(printPath);

            for (int i = 0; i < 400; i++)
            {
                text = "Line " + i;

                tw.WriteLine(text);

                Thread.Sleep(200);
            }

            tw.Close();
            await Task.CompletedTask;
        });

        var c = task.ContinueWith((antecedent) =>
        {
            taskID = task.Id;
            status = task.Status.ToString();

            try
            {
                string connString = WebConfigurationManager.ConnectionStrings["TaskContext"].ToString();
                sqlCommand.CommandText = "INSERT INTO dbo.TaskTable (TaskId, Status) VALUES (" + taskID + ", '" + status + "')";
                conn.ConnectionString = connString;
                sqlCommand.Connection = conn;
                conn.Open();
                sqlCommand.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                String info = ex.Message + ex.ToString() + ex.StackTrace;

                throw new Exception("SQL Issue" + info);
            }
            finally
            {
                if (conn != null)
                {
                    conn.Close();
                    conn = null;
                }
                sqlCommand = null;
            }


        });

    });

    return View();
}
like image 953
Jason P Sallinger Avatar asked Mar 27 '18 17:03

Jason P Sallinger


1 Answers

I think you are missing a point here.

When you use MVC you are in a complete "stateless" situation where the async Task is submit and executed by a different Thread, so you loose control of it. The only way that you have to "get notified" about the change of a task is to get an "event" from that task. An event will inform you about the "Status Changed". The Task class does not raise events but has the ContinueWith method. The proper way would be to:

  1. Create and start the Task and store its Status somewhere (DB, text file, session)
  2. Use the ContinueWith to upload the status of the Task in your data store
  3. Continuously query the Datastore to get an update version of the Status of all queued Tasks

I think in this thread they tried to achieve the same: How to track if an async/awaitable task is running

like image 101
Raffaeu Avatar answered Nov 11 '22 07:11

Raffaeu