Suppose I have the following legacy service:
public interface ISomeService
{
event EventHandler<SomeEventArgs> SomethingHappened;
void MakeSomethingHappen();
}
Now I have another service that relies on the one above, but uses async reporting, as follows:
public class OtherService : IOtherService
{
public async Task DoSomethingElse(IProgress<SomeEventArgs> progress)
{
// ...
}
}
I need to capture the events triggered in ISomeService and report them in OtherService using IProgress<T>. My solution to this was:
public async Task DoSomethingElse(IProgress<SomeEventArgs> progress)
{
void handleEvent(object sender, SomeEventArgs e)
{
if (progress != null)
progress.Report(e);
}
_someService.SomethingHappened += progressHandler;
try
{
_someService.MakeSomethingHappen();
}
finally
{
_someService.SomethingHappened -= progressHandler;
}
}
Are there any dangers/problems with this?
No, there is no problem with consuming an event by a Progress<T> object. The only problem would be a possible memory leak in case you forgot to unsubscribe from the event, but your code takes care of that.
I would also like to point out that if you want to get fancy you could do the same thing in a reactive way, by using an IObservable<SomeEventArgs> as an intermediate propagator:
public async Task DoSomethingElse(IProgress<SomeEventArgs> progress)
{
using var subscription = Observable
.FromEventPattern<SomeEventArgs>(
h => _someService.SomethingHappened += h,
h => _someService.SomethingHappened -= h)
.Select(ep => ep.EventArgs)
.Subscribe(progress.ToObserver());
await Task.Run(() =>
{
_someService.MakeSomethingHappen();
});
}
The FromEventPattern, Select and ToObserver are extension methods defined in the System.Reactive package.
Is there a reason you're avoiding Lambda functions? I can't think of any immediate problem with this solution (aside from binding the event to a local handler which may keep the parent in memory even when "MakeSomethingHappen" results in "_someService" own demise).
Still, for the sake of readability, I would go with Lambda handler...
Related
I have a class that handles some hardware. I want this class to check some state of the hardware every N milleseconds, and fire an event if a certain case is true.
I would like the interface on the outside to be such:
var rack = new Rack();
rack.ButtonPushed += OnButtonPushed;
rack.Initialise();
I want to avoid using "real" threads, but I don't seem to get the Task correctly (for testing, I just fire the event every second):
class Rack {
public event EventHandler ButtonPushed;
public void Initialise()
{
Task.Run(() =>
{
while (true)
{
Task.Delay(1000);
ButtonPushed?.Invoke(this, null);
}
});
}
}
But this doesn't work, the events get fired all at the same time.
Is Task the right thing to use here, or should I work with a Timer, or a thread?
Task delay returns a task that finishes execution after given time and needs to be awaited. In order to await it we'd need to mark the delegate as async and add await before the call:
class Rack {
public event EventHandler ButtonPushed;
public void Initialise()
{
Task.Run(async () =>
{
while (true)
{
await Task.Delay(1000);
ButtonPushed?.Invoke(this, null);
}
});
}
}
Is Task the right thing to use here, or should I work with a Timer, or
a thread?
Well, your approach is just as good as using Timer. Thread could be used as well but it's a bit too low-level approach.
As others mentioned it would be better with CancellationToken, this allows us to cancel the task later so it finishes execution:
while (!cancellationToken.IsCancellationRequested)
{
...
}
I understand you should try not to do async void with the exception of event handlers. Does that exception of event handlers include Prism events? Is my example below ok to do?
Module A's View has a DataGrid and when the user clicks a record it publishes the SelectionChangedEvent, the event is subscribed in Module B and might take a while to complete.
public class ModuleA
{
private ObservableCollection<Company> carCollectionOC = new ObservableCollection<Company>();
private void CarCollection_CurrentChanged(object sender, EventArgs e)
{
Company company = (Company)(sender as ICollectionView).CurrentItem;
this.eventAggregator.GetEvent<SelectionChangedEvent>().Publish(company.Brand);
}
}
Module B subscribes and does async void.
public class ModuleB
{
private ObservableCollection<Vehicle> vehicleOC = new ObservableCollection<Vehicle>();
private VehicleService vehicleService;
public ModuleB(IEventAggregator eventAggregator)
{
this.vehicleService = new VehicleService();
this.eventAggregator.GetEvent<SelectionChangedEvent>()
.Subscribe(this.SubscribedMethod,
ThreadOption.UIThread,
false);
}
private async void SubscribedMethod (string brand)
{
this.vehicleOC = await this.GetData(brand);
}
private List<Vehicle> GetVehicles(string carBrand)
{
Console.WriteLine("finishing GetVehicles");
return this.vehicleService.GetVehicleList(carBrand);
}
private async Task<ObservableCollection<Vehicle>> GetData(string carBrand)
{
vehicleListFromService = await Task.Run(() => this.GetVehicles(carBrand));
this.vehicleOC.Clear();
foreach (var vehicle in vehicleListFromService)
this.vehicleOC.Add(vehicle);
return this.vehicleOC;
}
}
Certainly, even stephen clearly (an authority on this subject as far as i am concerned) says:
To summarize this first guideline, you should prefer async Task to
async void. Async Task methods enable easier error-handling,
composability and testability. The exception to this guideline is
asynchronous event handlers, which must return void. This exception
includes methods that are logically event handlers even if they’re not
literally event handlers (for example, ICommand.Execute
implementations).
https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming
Personally, I always wrap the contents of an async void method in a try-catch and deal with the exception, regardless of the method's contents. I've written a simple helper class to aid with that.
Does that exception of event handlers include Prism events?
Yes. The subscriber has to be Action, that means void MyHandler() (or async void MyHandler()).
I am currently building out a custom task manager and I'm wondering if it's possible to tell the task manager to listen for a specific event (OnSomething below), and then invoke a callback method when the task raises that event. However, mentally I can't see how it's possible to listen for an event that doesn't exist at the base class level. For example, I have a base class that contains basic information about the task called CustomTask:
public abstract class CustomTask {
public bool IsRunning { get; private set; } = false;
public void Start() {
IsRunning = true;
DoSomething();
IsRunning = false;
}
protected abstract void DoSomething();
}
For the sake of SO readers, I've simplified the definition, but you get the gist of it. It contains basic details, a few methods for starting and canceling, provides basic state management (simplified IsRunning here), etc.
I then have custom tasks that derive from CustomTask, in this case, let's focus on a sample task called CustomTaskA. It contains a definition for an event called OnSomething, which someone, somewhere may want to listen for:
public sealed class CustomTaskA : CustomTask {
protected override void DoSomething() => RaiseOnSomething(this, new EventArgs());
public event EventHandler<EventArgs> OnSomething;
private void RaiseOnSomething(object sender, EventArgs e) => OnSomething?.Invoke(sender, e);
}
Now, the CustomTaskManager registers tasks, tracks them via Guid, manages them and more, but for simplicity:
public sealed class CustomTaskManager {
// Singleton setup.
private static CustomTaskManager _instance = new CustomTaskManager();
public static CustomTaskManager Instance {
get {
// Simplified for SO.
if (_instance == null)
_instance = new CustomTaskManager();
return;
}
}
// Collection of tasks.
private Dictionary<Guid, CustomTask> _tasks = new Dictionary<Guid, CustomTask>();
// Register and start a task.
public bool TryRegisterAndStartTask(CustomTask task, out Guid taskId) {
taskId = Guid.Empty;
try {
// Register task.
taskId = Guid.NewGuid();
_tasks.Add(taskId, task);
// Listen for events.
// Start task.
task.Start();
} catch (Exception e) {
// Log exception.
}
return false;
}
}
When registering and starting a task, I'd like to tell the task manager I want to listen for OnSomething, and if OnSomething is invoked, I want the task manager to call a method OnSomethingWasRaised. For example:
TaskManager.Instance.TryRegisterAndStartTask(task, out Guid taskId, task.OnSomething, OnSomethingWasRaised);
private static void OnSomethingWasRaised(object sender, EventArgs e) {
Console.WriteLine("Woohoo!");
}
I know the specifying and invoking a callback method is entirely possible, and listening for events is plausible with reflection.
Is there a way (with or without using reflection) to listen for a specified event defined on a derived object and then invoke a specified callback method?
NOTE: Please excuse any syntactical errors as I hand-typed the snippets to keep them minimal.
Problem with (proposed) approach like this:
TryRegisterAndStartTask(task, out Guid taskId, task.OnSomething, OnSomethingWasRaised);
is that you cannot pass event as argument, or store it in variable, because event is just a set of two methods (add and remove), just like property is a set of two methods get and set.
You can of course change event to "raw" delegate:
public EventHandler<EventArgs> OnSomething;
This one you can pass by reference:
public bool TryRegisterAndStartTask(CustomTask task, ref EventHandler<EventArgs> del, EventHandler<EventArgs> sub, out Guid taskId) {
taskId = Guid.Empty;
// subscribe
del += sub;
...
}
CustomTaskManager.Instance.TryRegisterAndStartTask(task, ref task.OnSomething, OnSomethingWasRaised, out var taskId);
But that's usually not a good idea, since you are losing private scope of events - with events one can only add\remove delegates, with raw delegate anyone can do anything, like invoking or setting to null.
If regular event stays - that means reflection is the only way to achieve your goal, and even worse - you'll have to reference to the event you want to subscribe to by string name, not by an actual reference, though you can use nameof(task.OnSomething). Then, you are losing compile time validation of subscription delegate type. Say you want to subscribe to event Action Something but passing Func<string> delegate there. It will compile fine with reflection approach, and fail only at runtime.
Still if you insist that will look something like this:
public bool TryRegisterAndStartTask(CustomTask task, string eventName, Delegate sub, out Guid taskId) {
taskId = Guid.Empty;
// subscribe
var ev = task.GetType().GetEvent(eventName, BindingFlags.Public | BindingFlags.Instance);
var addMethod = ev.GetAddMethod(); // this can be null or private by the way
addMethod.Invoke(task, new [] {sub});
...
}
And called like this:
var task = new CustomTaskA();
EventHandler<EventArgs> handler = OnSomethingWasRaised;
CustomTaskManager.Instance.TryRegisterAndStartTask(task, nameof(task.OnSomething), handler, out var taskId);
Ugly, unsafe, and not worth it in your scenario, in my opinion.
I have an WPF application based on MVVM architecture. I am implementing the common and widely used INotifyPropertyChanged interface on my ViewModels, because I need to react on user interaction.
But how do I perform an asynchronous action (e.g. loading some data) from within the synchronous PropertyChanged event handler without using async void 'hacks'?
Thanks in advance!
EDIT
The main reason why i need to avoid async void is because I am working in an test driven environment. Async void methods are not testable :(
Actually, this is not about async void.
Usually you want to fire async operation and let your property setter return.
Sample snippet:
private string carManufacturerFilter;
public string СarManufacturerFilter
{
get { return carManufacturerFilter; }
set
{
if (carManufacturerFilter != value)
{
carManufacturerFilter = value;
OnPropertyChanged();
// fire async operation and forget about it here;
// you don't need it to complete right now;
var _ = RefreshCarsListAsync();
}
}
}
private async Task RefreshCarsListAsync()
{
// call some data service
var cars = await someDataService.GetCarsAsync(carManufacturerFilter)
.ConfigureAwait(false);
// ...
}
Note, that there are a lot of things to add here:
since this is fire-and-forget approach, you need to block user input until operation is running. In other words, there should be some sort of busy indicator;
you may want to delay async operation firing. This is usually applicable, when there are string properties. You don't want to fire async operation after each char typed by user. Instead it's desirable to wait user to complete input;
there could be several properties, which fire the same async operation (imagine complex data filter). Some of them should fire operation immediately (e.g. checkbox), some of them need delay before firing;
you need to handle exceptions inside async method and display errors somehow.
P.S. I strongly recommend you to take a look at Reactive UI.
The reason async void is supported is to allow using await in event handlers, which are usually void.
If you want it to be testable, write the entire code in another async Task method and have the event handler call it directly. Test this method in your tests.
void OnPropertyChanged(PropertyChangedEventArgs e)
{
OnPropertyChangedAsync(e)
}
// Test this method
async Task OnPropertyChangedAsync(PropertyChangedEventArgs e)
{
...
}
Little late to the party but I'd say this. First, notice that INotifyPropertyChanged says that your view model will implement
public event PropertyChangedEventHandler? PropertyChanged;
Secondly, if you inspect PropertyChangedEventHandler, you'll see that it's a delegate, of course:
public delegate void PropertyChangedEventHandler(object? sender, PropertyChangedEventArgs e);
So why not implement your own delegate using Func<>? For example, if you're looking to stick with the (object? sender, PropertyChangedEventArgs e) parameter pattern that PropertyChanged uses, then I would implement something like
public Func<object?, PropertyChangedEventArgs, Task>? PropertyChangedAsync { get; set; }
in the same place (or in place of) public event PropertyChangedEventHandler? PropertyChanged;.
From there you can "wire it up" just as you would PropertyChanged:
ViewModel.PropertyChanged += OnViewModelPropertyChanged;
ViewModel.PropertyChangedAsync += OnViewModelPropertyChangedAsync;
private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Do something (synchronously) with the notification
}
private Task OnViewModelPropertyChangedAsync(object? sender, PropertyChangedEventArgs e)
{
// Do something (asynchronously) with the notification
return Task.CompletedTask;
}
By the way, if you really want to get fancy, you could also create a custom PropertyChangedEventHandler event with an interface very similar to INotifyPropertyChanged. Obviously, the only difference is you'd want to return Task or Task<T> instead of void. The solution that I mentioned above using Func<object?, PropertyChangedEventArgs, Task> is just a lot more concise (one line).
You can't. INotifyPropertyChanged doesn't support async calls. You need to do a hack, or rethink you strategy.
INotifyPropertyChanged is not intended for async actions. Its goal is to enable a class notify the UI that its data has changed. The UI works in a dedicated thread, so cross-thread operations must be avoided.
You should use the "dreaded" async void approach.
You can also use Dispatcher.BeingInvoke (async () => { … await …} ), but it will be the same as using async void.
I have a class with properties that are bound to my view. To keep my view up-to-date, I implement INotifyPropertyChanged and raise the event everytime some property changes.
Now I got some heavy functions that freeze my application. I want to put them into a background task.
First: here my current approach
(e.g. on button click)
private async void HeavyFunc()
{
foreach (var stuff)
{
count += await Task.Run(() => stuff.Fetch());
}
if (count == 0)
//...
}
stuff class
public async Task<int> Fetch()
{
//network stuff
RaisePropertyChanged("MyProperty");
}
public async void RaisePropertyChanged(string pChangedProperty)
{
await Application.Current.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Normal,
new ThreadStart(() =>
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(pChangedProperty);
}
);
}
The code above gives an exception ("DependencySource" must be created in the same thread like "DependencyObject").
AFAIK, you generally need to create a new thread and run it (while awaiting it). ´await Task.Run(...);´ should do this job.
Since the PropertyChanged event directly influences the UI, calling it in the UI thread seems to be a good decision. This is why I call Dispatcher.BeginInvoke.
What I don't understand: the exception above is caused when different threads are responsible for the data. But I explicitely calling the event on my UI-thread and the object should be created by the UI-thread too. So why do I get an exception?
My main question is: How do I implement the events for the INotifyPropertyChanged interface generally to avoid or handle most of the async programming problems like above? What should be considered while constructing the functions?
Now I got some heavy functions that freeze my application.
If you're really doing asynchronous "network stuff", then it shouldn't be freezing the app.
My main question is: How do I implement the events for the INotifyPropertyChanged interface generally to avoid or handle most of the async programming problems like above?
The approach that I prefer is to not handle this in the event raising code. Instead, structure the rest of your code so that it respects the UI layer.
In other words, divide your "service" (or "business logic") code from your "UI" code so that it works like this:
// In StuffService class:
public async Task<Result> FetchAsync()
{
//network stuff
return result;
}
// In StuffViewModel class:
public async void ButtonClicked()
{
foreach (var stuff)
{
var result = await Task.Run(() => _stuffService.FetchAsync());
MyProperty = result.MyProperty;
count += result.Count;
}
if (count == 0)
//...
}
public Property MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
RaisePropertyChanged();
}
}
private void RaisePropertyChanged([CallerMemberName] string pChangedProperty = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(pChangedProperty));
}
This way, there's no manual thread jumping, all properties have the standard ViewModel implementation, the code is simpler and more maintainable, etc.
I did leave in the call to Task.Run, although this should be superfluous if your network calls are truly asynchronous.