keeping track of completion of many tasks - wpf

I have a WPF application in which lots of event initiate tasks. Here's how I am doing it. But I am not happy about how it looks now
var task = UpdatePersonModelAsync();
taskCollection.Add(task);
RaisePropertyChanged(nameof(IsUpdateInProgress));
await task;
taskCollection.Remove(task);
RaisePropertyChanged(nameof(IsUpdateInProgress));
The property which shows/hides spinner
public bool IsUpdateInProgress => taskCollection.Count > 0;
I was going through the Progress<T> it seems like a call back.
When all the incoming tasks are completed a small spinner will be hidden.

You probably should use await Task.WhenAll(taskCollection.ToArray()); for waiting all the tasks you need to wait. After that, put the code for hiding the spinner below the await statement.

I tried using a CustomTaskScheduler. I got it from https://www.infoworld.com/article/3063560/application-development/building-your-own-task-scheduler-in-c.html
http://www.codeguru.com/csharp/article.php/c18931/Understanding-the-NET-Task-Parallel-Library-TaskScheduler.htm
As tasks are created from various calls, I use the CustomTaskScheduler in Task.Factory.StartNew.
On debugging, I can get hits on QueueTask but Execute isn't getting called. What am I missing?
public sealed class CustomTaskScheduler : TaskScheduler, IDisposable
{
public delegate void TaskStartedHandler(Task sender);
public delegate void AllTasksCompletedHandler(IEnumerable<Task> sender);
public event TaskStartedHandler TaskStarted;
public event AllTasksCompletedHandler AllTasksCompleted;
private BlockingCollection<Task> tasksCollection = new BlockingCollection<Task>();
private readonly Thread mainThread = null;
public CustomTaskScheduler()
{
mainThread = new Thread(new ThreadStart(Execute));
if (!mainThread.IsAlive)
{
mainThread.Start();
}
}
private void Execute()
{
foreach (var task in tasksCollection.GetConsumingEnumerable())
{
var isStarted = TryExecuteTask(task);
if (isStarted && TaskStarted != null)
{
TaskStarted(task);
}
}
if(tasksCollection.GetConsumingEnumerable().All(m => m.IsCompleted))
{
AllTasksCompleted?.Invoke(tasksCollection.GetConsumingEnumerable());
}
}
protected override IEnumerable<Task> GetScheduledTasks()
{
return tasksCollection.ToArray();
}
protected override void QueueTask(Task task)
{
if (task != null)
{
tasksCollection.Add(task);
}
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
return false;
}
private void Dispose(bool disposing)
{
if (!disposing)
{
return;
}
tasksCollection.CompleteAdding();
tasksCollection.Dispose();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
The code is not yet complete. It would be great if someone can point to right direction

Here's a new version
public class TaskTracker
{
public delegate void AllTasksCompletedHandler(object sender);
public delegate void TaskStartedHandler();
public event AllTasksCompletedHandler AllTasksCompleted;
public event TaskStartedHandler TaskStarted;
private object syncLock = new object();
private SynchronizedCollection<Task> tasksCollection;
private bool isTaskStartedNotified;
private readonly uint delay;
public TaskTracker(uint delayBeforeRemovingTasks)
{
tasksCollection = new SynchronizedCollection<Task>();
delay = delayBeforeRemovingTasks;
}
public void Add(Task task)
{
if (!isTaskStartedNotified)
{
isTaskStartedNotified = true;
TaskStarted?.Invoke();
}
task.ContinueWith(t =>
{
RemoveTask(t);
});
tasksCollection.Add(task);
}
private async void RemoveTask(Task task)
{
await Task.Delay(300);
await Task.Run(() =>
{
tasksCollection.Remove(task);
if (tasksCollection.Count == 0)
{
isTaskStartedNotified = false;
AllTasksCompleted?.Invoke(tasksCollection);
}
});
}
}

Related

How can I run a Dispatcher.Invoke() in a .NET Core 3 class library?

I am developing a WPF/NetCore3.1 application with MVVM. In the view there is a button which is bound to a RelayCommand. The ViewModel is in a different class library than the View. In the ViewModel a timer is started which increments a variable every second and triggers the CanExecuteChanged event of the RelayCommand.
Here is my ViewModel:
public ImportExportViewModel()
{
MakeOfferCommand = new RelayCommand(MakeOffer, CanMakeOffer);
Timer t = new Timer(1000);
t.Elapsed += T_Elapsed;
t.Start();
}
private void T_Elapsed(object sender, ElapsedEventArgs e)
{
ElapsedTime++;
MakeOfferCommand.RaiseCanExecuteChanged();
}
private void MakeOffer()
{
// TODO Make Offer
}
private bool CanMakeOffer()
{
return ElapsedTime < 60;
}
And here the RaiseCanExecuteChanged:
public void RaiseCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null)
{
handler(this, new EventArgs());
}
}
But here I get an InvalidOperationException: the calling thread cannot access this object because the object is owned by another thread.
Normally I would execute a Dispatcher.Invoke() here, but that seems not to exist in .NetCore3.1.
Can anyone tell me how I can still make cross-thread calls?
You could inject your view model with an IDispatch interface that you implement in each platform:
Interface:
public interface IDispatch
{
bool CheckAccess();
void Invoke(Action action);
}
View Model:
public IDispatch Dispatch { get; set; }
private void T_Elapsed(object sender, ElapsedEventArgs e)
{
if (Dispatch != null && !Dispatch.CheckAccess())
Dispatch.Invoke(new Action(() => { /* do something */ }));
...
}
WPF implementation:
public class WpfDispatch : IDispatch
{
private readonly Dispatcher _dispatcher;
public WpfDispatch(Dispatcher dispatcher) =>
_dispatcher = dispatcher;
public bool CheckAccess() => _dispatcher.CheckAccess();
public void Invoke(Action action) => _dispatcher.Invoke(action);
}

Getting the next turn/direction in Mapbox

I'm trying to get the direction of the upcoming turn while travelling, i.e. I want to trigger an event in my app according to the direction of the upcoming turn.
I've tried using event listeners, taking help of the documentation and the provided examples but as I'm pretty new to android studio and mapbox, I've not been successful (my app either crashed or the function would never get triggered). I've also tried searching for getting the voice commands into text form or log form but have failed.
While my current code does display directions and gives voiced instructions, I can't figure out how to access either of them. I'd like to know if there's a simple way of achieving what I'm after without using any event listeners.
private MapView mapView;
private MapboxMap mapboxMap;
private PermissionsManager permissionsManager;
private LocationComponent locationComponent;
private DirectionsRoute currentRoute;
private static final String TAG = "DirectionsActivity";
private NavigationMapRoute navigationMapRoute;
private MapboxNavigation navigation;
private Button button;
private NavigationView navigationView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(this, getString(R.string.access_token));
setContentView(R.layout.activity_main);
mapView = findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(this);
// Toast.makeText(this, "Hello", Toast.LENGTH_SHORT).show();
}
#Override
public void onMapReady(#NonNull final MapboxMap mapboxMap) {
this.mapboxMap = mapboxMap;
//Toast.makeText(this, "Hello", Toast.LENGTH_SHORT).show();
mapboxMap.setStyle(getString(R.string.navigation_guidance_day), new Style.OnStyleLoaded() {
#Override
public void onStyleLoaded(#NonNull Style style) {
enableLocationComponent(style);
addDestinationIconSymbolLayer(style);
mapboxMap.addOnMapClickListener(MainActivity.this);
button = findViewById(R.id.startButton);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
boolean simulateRoute = true;
NavigationLauncherOptions options = NavigationLauncherOptions.builder()
.directionsRoute(currentRoute)
.shouldSimulateRoute(simulateRoute)
.build();
NavigationLauncher.startNavigation(MainActivity.this, options);
}
});
}
});
}
private void addDestinationIconSymbolLayer(#NonNull Style loadedMapStyle) {
loadedMapStyle.addImage("destination-icon-id",
BitmapFactory.decodeResource(this.getResources(), R.drawable.mapbox_marker_icon_default));
GeoJsonSource geoJsonSource = new GeoJsonSource("destination-source-id");
Log.d(TAG, "addDestinationIconSymbolLayer: " + geoJsonSource);
loadedMapStyle.addSource(geoJsonSource);
SymbolLayer destinationSymbolLayer = new SymbolLayer("destination-symbol-layer-id", "destination-source-id");
destinationSymbolLayer.withProperties(
iconImage("destination-icon-id"),
iconAllowOverlap(true),
iconIgnorePlacement(true)
);
loadedMapStyle.addLayer(destinationSymbolLayer);
}
#SuppressWarnings( {"MissingPermission"})
#Override
public boolean onMapClick(#NonNull LatLng point) {
Point destinationPoint = Point.fromLngLat(point.getLongitude(), point.getLatitude());
Point originPoint = Point.fromLngLat(locationComponent.getLastKnownLocation().getLongitude(),
locationComponent.getLastKnownLocation().getLatitude());
GeoJsonSource source = mapboxMap.getStyle().getSourceAs("destination-source-id");
Log.d(TAG, "Does this even work");
Log.d(TAG, "onMapClick: " + source.toString());
if (source != null) {
source.setGeoJson(Feature.fromGeometry(destinationPoint));
}
getRoute(originPoint, destinationPoint);
button.setEnabled(true);
button.setBackgroundResource(R.color.mapboxBlue);
return true;
}
private void getRoute(Point origin, Point destination) {
NavigationRoute.builder(this)
.accessToken(Mapbox.getAccessToken())
.origin(origin)
.destination(destination)
.build()
.getRoute(new Callback<DirectionsResponse>() {
#Override
public void onResponse(Call<DirectionsResponse> call, Response<DirectionsResponse> response) {
Log.d(TAG, "Response code: " + response.code());
if (response.body() == null) {
Log.e(TAG, "No routes found, make sure you set the right user and access token.");
return;
} else if (response.body().routes().size() < 1) {
Log.e(TAG, "No routes found");
return;
}
currentRoute = response.body().routes().get(0);
if (navigationMapRoute != null) {
navigationMapRoute.removeRoute();
} else {
navigationMapRoute = new NavigationMapRoute(null, mapView, mapboxMap, R.style.NavigationMapRoute);
}
navigationMapRoute.addRoute(currentRoute);
}
#Override
public void onFailure(Call<DirectionsResponse> call, Throwable throwable) {
Log.e(TAG, "Error: " + throwable.getMessage());
}
});
}
#SuppressWarnings( {"MissingPermission"})
private void enableLocationComponent(#NonNull Style loadedMapStyle) {
if (PermissionsManager.areLocationPermissionsGranted(this)) {
locationComponent = mapboxMap.getLocationComponent();
locationComponent.activateLocationComponent(this, loadedMapStyle);
locationComponent.setLocationComponentEnabled(true);
locationComponent.setCameraMode(CameraMode.TRACKING);
} else {
permissionsManager = new PermissionsManager(this);
permissionsManager.requestLocationPermissions(this);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
#Override
public void onExplanationNeeded(List<String> permissionsToExplain) {
Toast.makeText(this, R.string.user_location_permission_explanation, Toast.LENGTH_LONG).show();
}
#Override
public void onPermissionResult(boolean granted) {
if (granted) {
enableLocationComponent(mapboxMap.getStyle());
} else {
Toast.makeText(this, R.string.user_location_permission_not_granted, Toast.LENGTH_LONG).show();
finish();
}
}
// Add the mapView's own lifecycle methods to the activity's lifecycle methods
#Override
public void onStart() {
super.onStart();
mapView.onStart();
}
#Override
public void onResume() {
super.onResume();
mapView.onResume();
// Toast.makeText(this, "Hello", Toast.LENGTH_SHORT).show();
}
#Override
public void onPause() {
super.onPause();
mapView.onPause();
}
#Override
public void onStop() {
super.onStop();
mapView.onStop();
}
#Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
#Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
}
It sounds like you might want to look at using an event listener for a custom milestone. Here's a link to the docs:
https://docs.mapbox.com/android/navigation/overview/milestones/#milestone-event-listener

Windows Service Best Loop Way?

I need a windows service to transfer data to another SQL server every 10 minutes.
This service is scheduled with a periodicity of 10 minutes and executes 5 stored procedures that transfer the data
If machine restarts the service should work again.
So, What's best way for this service?
My solution is as below. But this service will stop after the first time:
static class Program
{
static void Main()
{
#if DEBUG
Service1 myService = new Service1();
myService.OnDebug();
#else
var servicesToRun = new ServiceBase[]
{
new Service1()
};
ServiceBase.Run(servicesToRun);
#endif
}
public partial class Service1 : ServiceBase
{
Thread _worker;
static readonly AutoResetEvent StopRequest = new AutoResetEvent(false);
private static IDataRepository _dataRepository;
public Service1()
{
_dataRepository = new DataRepository();
InitializeComponent();
}
public void OnDebug()
{
OnStart(null);
}
protected override void OnStart(string[] args)
{
_worker = new Thread(DoWork);
_worker.Start();
}
protected override void OnStop()
{
StopRequest.Set();
_worker.Join();
}
private static void DoWork()
{
for (;;)
{
if (!_dataRepository.CheckInternetConnection()) return;
if (!_dataRepository.CheckDatabaseConnection()) return;
if (!_dataRepository.CheckOppositeDatabaseConnection()) return;
if (StopRequest.WaitOne(10000)) return;
List<Test> comeResults = _dataRepository.CheckNewDataCashRegister();
if (comeResults == null) return;
bool sendTest = _dataRepository.SendTest(comeResults);
if (!sendTest) return;
}
}
}
If you want to minimize external dependencies like using the Task Scheduler you can schedule the work in the service like this:
public partial class Service1 : ServiceBase
{
private readonly CancellationTokenSource _cancellationTokenSource;
private static IDataRepository _dataRepository;
public Service1()
{
_dataRepository = new DataRepository();
_cancellationTokenSource = new CancellationTokenSource();
InitializeComponent();
}
public void OnDebug()
{
OnStart(null);
}
protected override void OnStart(string[] args)
{
ScheduleWorkAsync();
}
protected override void OnStop()
{
_cancellationTokenSource.Cancel();
_cancellationTokenSource.Dispose();
}
private async Task ScheduleWorkAsync()
{
var _cancellationToken = _cancellationTokenSource.Token;
while (!_cancellationTokenSource.IsCancellationRequested)
{
try
{
DoWork();
await Task.Delay(TimeSpan.FromMinutes(10), _cancellationToken);
}
catch (OperationCanceledException)
{
}
}
}
private void DoWork()
{
if (!_dataRepository.CheckInternetConnection()) return;
if (!_dataRepository.CheckDatabaseConnection()) return;
if (!_dataRepository.CheckOppositeDatabaseConnection()) return;
List<Test> comeResults = _dataRepository.CheckNewDataCashRegister();
if (comeResults == null) return;
bool sendTest = _dataRepository.SendTest(comeResults);
if (!sendTest) return;
}
}

Timer stops working in Http Module

I am trying to use a System.Threading.Timer to perform a task every five seconds in a Http Module and the timer just randomly stops firing:
using Bookbyte.IpFilter.Manager;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
namespace Bookbyte.IpFilter
{
public class IpFilterModule : ApplicationStartModuleBase
{
private static object lockObject = new object();
private static Timer timer;
private static IpFilterManager filterManager;
private static IEnumerable<string> blacklist;
private static IEnumerable<string> whitelist;
public override void OnInit(HttpApplication context)
{
context.BeginRequest += (object sender, EventArgs e) =>
{
var app = sender as HttpApplication;
if (app != null)
{
var ipAddress = app.Request.UserHostAddress;
if (!whitelist.Any(x => x == ipAddress))
{
if (blacklist.Any(x => x == ipAddress))
{
app.Context.Response.StatusCode = 404;
app.Context.Response.SuppressContent = true;
app.Context.Response.End();
return;
}
}
}
};
base.OnInit(context);
}
public override void OnStart(HttpApplication context)
{
filterManager = new IpFilterManager();
blacklist = new List<string>();
whitelist = new List<string>();
timer = new Timer(new TimerCallback((object sender) =>
{
lock (lockObject)
{
blacklist = filterManager.GetBlacklist();
whitelist = filterManager.GetWhitelist();
}
}), null, 0, 5000);
base.OnStart(context);
}
public override void Dispose()
{
timer.Dispose();
base.Dispose();
}
}
}
And the ApplicationStartModuleBase:
using System.Web;
namespace Bookbyte.IpFilter
{
public abstract class ApplicationStartModuleBase : IHttpModule
{
private static volatile bool applicationStarted = false;
private static object applicationStartLock = new object();
public void Init(HttpApplication context)
{
if (!applicationStarted)
{
lock (applicationStartLock)
{
if (!applicationStarted)
{
this.OnStart(context);
applicationStarted = true;
}
}
}
this.OnInit(context);
}
public virtual void OnInit(HttpApplication context)
{
}
public virtual void OnStart(HttpApplication context)
{
}
public virtual void Dispose()
{
}
}
}
So I figured out the problem. The timer was being disposed in the module when the run time would call Dispose on the module.
public override void Dispose()
{
timer.Dispose();
base.Dispose();
}
Removing the call to timer.Dispose() fixed my issues.

Problems sending message between Silverlight apps

Has anyone had any issues communicating between two Silverlight apps. When I try to send a message from one app, I get an error, "The message could not be delivered to receiver." My code for sending is below. I'm using the similar code that is in the samples for implementing Windows Live ID in a Silverlight App. I have this working when I'm running locally, but when I post to the server, I'm getting the delivery error.
#region Fields
private readonly LocalMessageSender sender = new LocalMessageSender("LiveIdAuthentication");
private int attempts = 0;
private const int MAX_ATTEMPTS = 10;
#endregion
#region Constructors
public MainPage()
{
InitializeComponent();
this.sender.SendCompleted += new EventHandler<SendCompletedEventArgs>(Sender_SendCompleted);
this.SendMessage("authenticated");
}
#endregion
#region Event Handlers
private void Sender_SendCompleted(object sender, SendCompletedEventArgs e)
{
if (e.Error != null)
{
if (attempts > MAX_ATTEMPTS)
{
MessageBox.Show(e.Error.Message);
CloseWindow();
}
else
{
SendMessage("authenticated");
}
}
else
{
attempts = 0;
CloseWindow();
}
}
#endregion
#region Methods
private void SendMessage(string message)
{
attempts++;
this.sender.SendAsync(message);
}
private void CloseWindow()
{
HtmlPage.Window.Eval("window.open(\"about:blank\", \"_self\")");
HtmlPage.Window.Eval("window.close()");
}
#endregion
Sorry about forgetting the receiver. This is mostly from the Live ID example.
private readonly WindowsLiveIdAuthentication _service;
private readonly AsyncCallback _asyncCallback;
private readonly object _asyncState;
private readonly LocalMessageReceiver _receiver = new LocalMessageReceiver("LiveIdAuthentication");
private bool _isCompleted;
private LoadUserResult _result;
#region Constructors
public LoginAsyncResult(WindowsLiveIdAuthentication service, AsyncCallback asyncCallback, object asyncState)
{
this._service = service;
this._asyncCallback = asyncCallback;
this._asyncState = asyncState;
this._receiver.MessageReceived += this.LocalMessageReceived;
}
#endregion
#region Properties
public object AsyncState
{
get { return this._asyncState; }
}
public System.Threading.WaitHandle AsyncWaitHandle
{
get { throw new NotImplementedException(); }
}
public bool CompletedSynchronously
{
get { return false; }
}
public bool IsCompleted
{
get { return this._isCompleted; }
}
public LoadUserResult Result
{
get { return this._result; }
}
#endregion
#region Methods
public void Cancel()
{
if (!this._isCompleted)
{
this._isCompleted = true;
}
}
public void Complete()
{
if (!this._isCompleted)
{
this._isCompleted = true;
this._receiver.Dispose();
Application.Current.RootVisual.Dispatcher.BeginInvoke(() =>
{
if (this._asyncCallback != null)
{
this._asyncCallback(this);
}
});
}
}
#endregion
#region Event Handlers
public void HandleLoadCallback(IAsyncResult asyncResult)
{
this._result = this._service.EndLoadUser(asyncResult);
if (!this._result.User.Identity.IsAuthenticated && !this._isCompleted && (this != asyncResult.AsyncState))
{
this._receiver.Listen();
}
else
{
this.Complete();
if (Globals.CurrentUser == null)
{
Globals.CurrentUser = _result.User as User;
Globals.SelectedDate = DateTime.Now;
(App.Current.RootVisual as MainPage).SetTheme(Globals.CurrentUser.CurrentTheme);
HtmlPage.Window.Navigate(new Uri("#UserHome", UriKind.Relative));
}
}
}
private void LocalMessageReceived(object sender, MessageReceivedEventArgs e)
{
this._service.BeginLoadUser(this.HandleLoadCallback, this);
}
#endregion
UPDATE:
OK, I found out that a RIA service call had failed, which resulted in not calling receiver.Listen(). So there wasn't a receiver for the sender to send messages. I'm still working on the failed RIA service call, but that's a different issue. I'll mark this as answered.
Please also add the code for the receiver. That is a very important part of this. If the receiver doesn't exist, for example, then it will fail.

Resources