Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Design pattern for reporting/monitoring progress of long processes

Anyone can suggest a good Design Pattern for reporting/monitoring status/progress of long processes. Basically, I have a codebase that receives a "data-context" object:

public class DataContext : IDataContext
{
    pulbic Dictionary<string, objects> Properties { get; private set; }

    // Additional properties removed for simplicity...
}

Based on the provided context, a Task (not TPL-Task) object is created, with various subtasks. During execution, the DataContext object is passed to the various sub-tasks, which can retrieve or update it.

For example, let's say that the main task is a "Copy files" task. The DataContext will have properties like the SourceFolder and TargetFolder, and perhaps a FilterFiles property (e.g. *.docx). Our main task will be a CopyFilesTasks, and it will have a "pipeline" of subtasks - Scan Folders, Scan Files, Filter Files, Copy Files, etc....

What I am looking for, is the best way to allow the task/sub-tasks to report their progress to the caller/executer. In our example above, the changes in progress might be just "Copied file ABC.docx...", or perhaps something a bit more "complex", like "Scanning folder XYZ..."

I have considered the following options:

  1. INotifyPropertyChanged: add a "Progress" property to DataContext

    public string Progress { get; set { _progress = value; RaisePropertyChanged("Progress"); }

    and have the code that created the DataContext object register to the PropertyChanged event. However, this seems like a too-simplistic approach...

  2. ILog (using whatever logging framework you prefer): use an ILog instance in the various tasks/sub-tasks, and have the main-task executioner add it's own listener to the logging framework. However this seemed like bending the logging mechanism to do things it was not supposed to do.

  3. Udi Dahan's DomainEvents: The executioner of the task can regard the DataContext as a "domain", and therefore we can try to implement an "EventHandler" for a "ProgressChanged" event. In theory, this can be even used for more refined events, that happen in specific sub-tasks... But once again, it feels like forcing the concept...

My concerns include things like:

  • Progress might not be the only "event" that needs to be monitored - in our example above, we might want things more defined, like FolderHandled, FileCopied, etc., but we might not know the exact events when executing the tasks (remember - the subtasks are created based on the DataContext, and might result in different tasks being executed).
  • The context of running the tasks is not yet defined. For now, I'm just planning to run the tasks from the command-line application, so outputting to the command-line is needed for debugging. Later on, when I move this to a service, I might want to have a "listener" update a database with the task's progress (for example).
like image 231
SaguiItay Avatar asked Jun 20 '11 15:06

SaguiItay


People also ask

Which design pattern is implemented adopted when we want to keep track of real time location updates?

The Observer design pattern is the principle one used in MVC (model view controller) architecture.

What is a task pattern?

Task Patterns are designed as means of activity-centric sharing of knowledge work experience. They are seamlessly integrated in a semantic infrastructure that help preserve the work context and enables a Task Pattern life-cycle.


1 Answers

You can declare arguments for each possible operation type, say FileOperationEventArgs for file operation, DatabaseUpdateEventArgs for database operation etc.

public class FileOperationEventArgs : EventArgs
{
    public readonly string SourceFolder;
    public readonly string TargetFolder;

    public FileOperationEventArgs(string sourceFolder, string targetFolder)
    {
        SourceFolder = sourceFolder;
        TargetFolder = targetFolder;
    }
}

public class DatabaseUpdateEventArgs : EventArgs
{
    public readonly int RowsUpdated;

    public DatabaseUpdateEventArgs(int rowsUpdated)
    {
        RowsUpdated = rowsUpdated;
    }
}

OperationProgress class declares events for each operation type.

public class OperationProgress
{
    public event EventHandler<FileOperationEventArgs> FileCopied;
    public event EventHandler<DatabaseUpdateEventArgs> DatabaseUpdated;

    public void OnFileCopied(FileOperationEventArgs a)
    {
        if(FileCopied != null)
            FileCopied(this, a);
    }

    public void OnDatabaseUpdated(DatabaseUpdateEventArgs a)
    {
        if (DatabaseUpdated != null)
            DatabaseUpdated(this, a);
    }
}

OperationProgress will be specified when DataContext is created.

public class DataContext : IDataContext
{
    public Dictionary<string, object> Properties { get; private set; }
    public OperationProgress Progress { get; private set; }

    public DataContext(OperationProgress progress)
    {
        Progress = progress;
    }
}

Subtask implementation can update the progress.

public class FileCopySubTask
{
    public void Execute(DataContext context)
    {
        context.Progress.OnFileCopied(new FileOperationEventArgs("c:/temp1", "c:/temp2"));
    }
}
like image 193
Vlad Sukhachev Avatar answered Dec 04 '22 04:12

Vlad Sukhachev