I have a class Step that stores a list of Tasks :
public class Step
{
public string StepName { get; set; }
private string _stepStatus
public List<Task> Tasks { get; set; }
public string StepStatus
{
get
{
return _stepStatus;
}
set
{
_stepStatus = value;
}
}
public Step(String name)
{
StepName = name;
Tasks = new List<Task>();
}
I have a class of Task :
public class Task
{
public string TaskName { get; set; }
private string _taskStatus
public string TaskStatus
{
get
{
return _taskStatus;
}
set
{
// raise an event here
_taskStatus = value;
}
}
public Task(String name)
{
TaskName = name;
}
Whenever an individual updates a Task , I want to raise an event . this event should be caught by the parent i.e Step .. Step should check the status of all the tasks .
If all tasks' status are New , then Step status should be automatically set to New .
If all tasks' status are Finished , then Step status should be automatically set to Finished.
If even on of the task's status is In Progress , the Step Status should be set to In Progress.
I shall call this method TripleLogic , say.
I can do the logic for the above three since this is logical. I am not sure how to raise an event from Task Status setter and ensure that Step class object picks it up since I am new to C#. If someone could help me with this event raising part with a small sample code , and also show me how I cna get my parent Step object to capture this event and call TripleLogic at that instance.
I looked at INotifyPropertyChanged but am not sure how to implement.
Any other code design related pointers will be helpful.
First suggestion is not not expose your Tasks property on the Step class so that consumers are not able to modify the collection whenever/however they want. Instead add a method called AddTask(Task t) to Step and in there you can set up your event handling.
The Task class should expose an event, perhaps called StatusChanged. When the setter on the TaskStatus property is called, fire that event (after checking to ensure there are listeners). This event's arguments should include the updated status and the task's name. If you have a finite set of statuses, I'd suggest defining an enum for them, instead of relying on strings.
Example:
public class Task
{
public event EventHandler<TaskStatusEventArgs> StatusChanged;
//...
}
public enum TaskStatus
{
New,
InProgress,
Done
}
public class TaskStatusEventArgs: EventArgs
{
public string TaskName { get; private set; }
public TaskStatus Status { get; private set; }
public TaskStatusEventArgs(string taskName, TaskStatus status)
{
TaskName = taskName;
Status = status;
}
}
Back in Step and the AddTask() method, subscribe to the Task's StatusChanged event and keep track of the number of tasks in the Step. In the handler for the StatusChanged event, you can then grab the task's new status and do whatever logic you need in your TripleLogic() method.
Example:
public class Step
{
List<Task> tasks;
public void AddTask(Task t)
{
t.StatusChanged += HandleStatusChanged;
}
private void HandleStatusChanged(object sender, TaskStatusEventArgs args)
{
string taskName = args.TaskName;
TaskStatus status = args.Status;
TripleLogic(taskName, status);
}
}
Given that you've mentioned you are new to C#, perhaps go through the MSDN guide for events, specifically the sections on subscribing and publishing. The publishing section mentions the generic EventHandler<TEventArgs> delegate, I highly suggest using that method.
Your question is vague and large-enough that, when coupled with your lack of experience in the language, this may take you a while to get through.
Related
I'm trying to write something similar to Sagas using RX.NET. I've came across a simple issue and I don't know what the best way is to sychronize states by correlation id. I have simple EventAggregator which listens for some events and it can be notified from multiple threads so I cannot assume that subscribe is always called on the same thread so there is a possibility that the first notification will update the state and in the meantime another notification will receive old state and work with it so I have to synchronize it. I have simplified my scenario to something like below. I could lock the whole subscribe by some object id dependent but it doesn't seem right. What is RX way of doing it?
public interface IEventAggregator
{
IObservable<T> GetEvent<T>() where T : Event;
}
public class EventAggregator : IEventAggregator
{
Subject<Event> _sub = new Subject<Event>();
public IObservable<T> GetEvent<T>() where T : Event
{
return _sub.OfType<T>();
}
public void Notify<T>(T ev) where T : Event
{
_sub.OnNext(ev);
}
}
public class Event
{
public string CorrelationId { get; set; }
}
public class State
{
public int SomeValue { get; set; }
}
public interface IStateRepository
{
State Get(string id);
}
public class ProcessManager
{
private readonly IStateRepository _stateRepository;
private readonly IEventAggregator _eventAggregator;
public ProcessManager(IStateRepository stateRepository, IEventAggregator eventAggregator)
{
_stateRepository = stateRepository;
_eventAggregator = eventAggregator;
eventAggregator.GetEvent<Event>()
.Select(x => _stateRepository.Get(x.CorrelationId))
.Subscribe(state =>
{
// -do something with state like write or read;
// - state should be unique per correlation id.
// - this block should be synchronized.
});
}
}
How can I run some long operation, when my property raises PropertyChanged event?
Example:
class SomeClass : INotifyPropertyChanged
{
public ObservableConllection<Item> Items { get; set; }
public string Path
{
get => _path;
if (_path != value) {
_path = value;
OnPropertyChanged(nameof(Path));
// await Task.Run(()=> long operation, for example load some Items use "_path");
}
}
Property can't be async and I think it is "normal". But what should I do?
I think that such a case occurs often.
You should read Stephen Cleary's MSDN article about asynchronous data-bound properties where he defines a "task watcher" class called NotifyTaskCompletion<T> that implements the INotifyPropertyChanged interface and has a Result property that you can bind to:
Async Programming : Patterns for Asynchronous MVVM Applications: Data Binding: https://msdn.microsoft.com/en-us/magazine/dn605875.aspx
public class MainViewModel : INotifyPropertyChanged
{
public string Path
{
get { return _path; }
set
{
if (_path != value)
{
_path = value;
OnPropertyChanged(nameof(Path));
AsyncProperty = new NotifyTaskCompletion<int>(YourAsyncMethod());
}
}
}
public NotifyTaskCompletion<string> AsyncProperty { get; private set; }
}
<Label Content="{Binding AsyncProperty.Result}"/>
You can use Task.Run without await. It may or may not work well depending on the case, there is no universal answer regarding that.
You can create a method like this to set the value, it is a more sure way to do this. However if you need a databinding this can't work:
public async Task SetPath(string value)
{
if (_path != value) {
_path = value;
OnPropertyChanged(nameof(Path));
await Task.Run(()=> ...);
}
If you need data binding and want to use await, then it is usually the best practice to use debouncing with Timer - you don't want some expensive operation to be available without restriction to the user so that he can freeze or crash the app.
I have an event class that looks like this:
public delegate void OnEventTriggered();
public interface IEventClause
{
event OnEventTriggered onEventTriggered;
}
and another class that has a list of Events:
class Behaviour
{
IEnumerable<IEventClause> EventClauses { get; set; }
IEnumerable<IConditionClause> ConditionClauses { get; set; }
IEnumerable<IActionClause> ActionClauses { get; set; }
public void Initialize()
{
foreach (IEventClause eventClause in EventClauses)
{
eventClause.onEventTriggered += OnEventTriggered;
}
}
public void OnEventTriggered()
{
//TODO: OR, needs to figure a way to simulates AND
if (IsAllConditionsSatisfied())
{
RunAllActions();
}
}
}
Right now, all OnEventTriggered of the EventClauses will be listened by OnEvenTriggered on the Behaviour class, and this creates an OR effect, and I'm looking for an AND effect. How can I do this?
One way that I can think of is to have the events set a flag and use a polling mechanism to check if all the flags set by the events are true. I think it should be doable without explicitly polling(which I think is not neat) but I can't figure out how.
This is what I'd like to recreate: http://world-editor-tutorials.thehelper.net/triggers.php
but with the triggers getting fired only when all the events are met
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:
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...
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.
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).
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"));
}
}
Consider BackgroundWorkers.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
They have their own reportprogress event on a separate UI thread.
I put "volatile" because it's only vaguely so.
I have a class which has a property called StopRequested. This flag can be set by other threads at any time, and needs to indicate to my code that it should stop what it's doing and exit (this is a Windows Service based process, and when Stop is called, all processing needs to clean up and stop).
I wish to create some other classes to do the actual brunt of the processing work, however these classes also have to be aware of the "stop" flag. You can't just pass the flag because it will pass a copy, and you can't pass properties as ref types.
So how do you propagate a property that might change at any time into other classes?
The only thing I can think of is to pass a reference to the parent class, but I dislike coupling the worker classes to the parent for one flag. Any suggestions would be appreciated.
EDIT:
Here's a basic example:
public class A
{
public bool StopRequested { get; set; }
private Worker = new Worker();
public void DoWork();
{
worker.DoWork();
}
}
public class Worker
{
public void DoWork()
{
while(!StopRequested)
{
....
}
}
}
You could have each of your worker classes have their own StopRequest property and then just set that whenever StopRequest is flagged.
private List<IStopable> WorkerClasses = new List< IStopable > ()
public Bool StopRequest{
get
{
return _stopRequest;
}
set
{
_stopReqest = value;
foreach (var child in WorkerClasses)
child.StopRequest = value;
}
}
Like Rubens said, use an event. What you described basically defines event to a T:
Propagate a property change to other classes.
There is actually a facility in .NET that provides this already, albeit in a generic way: INotifyPropertyChanged. This interface provides a single event, PropertyChanged, that allows a class to notify any listeners of any property change.
In your case, you could easily provide your own interface that is more specific:
interface IStopNotifier
{
event EventHandler StopRequested;
}
This interface would be implemented by your main work manager (whatever it is), and could propagate itself like so:
class WorkManager: IStopNotifier
{
public event EventHandler StopRequested;
protected void OnStopRequested()
{
if (StopRequested != null) StopRequested(this, new EventArgs());
}
public void StopAllWorkers()
{
OnStopRequested();
}
public Worker CreateWorker<T>()
where T: Worker
{
var worker = new T(this);
return worker;
}
}
class abstract Worker: IDisposable
{
public Worker(IStopNotifier stopNotifier)
{
stopNotofier.StopRequested += HandleStopRequested;
}
private IStopNotifier m_stopNotifier;
private bool m_stopRequested = false;
internal void HandleStopRequested(object sender, EventArgs e)
{
m_stopRequested = true;
}
public void Dispose()
{
m_stopNotifier.StopRequested -= HandleStopRequested;
}
}
Why don't to create an event to handle stop requests?