Async Viewmodel constructor initializing data - c#

I've followed the pattern defined at Async OOP to create a view model which reads data from 2 sources. However, when I attempt to setup CollectionViewSource and Filters for the collection I get an exception Additional information: Must create DependencySource on same Thread as the DependencyObject. or when filtering the data I get the following exception The calling thread cannot access this object because a different thread owns it.
The properties are defined as follows
public NotifyTaskCompletion InitializationNotifier{ get; set; }
public Task Initialization => InitializationNotifier.Task;
The viewmodel constructor defines the InitializationNotifier as follows:
InitializationNotifier = new NotifyTaskCompletion(InitializeAsync());
The task is defined as below:
private async Task InitializeAsync()
{
var aDataSource = await Task.Run(() => ADataSource.Get(1, 2));
var bDataSource = await Task.Run(() => new BDataSource(1, 2);
PrepareData(aDataSource, bDataSource); // creates List<T> for different categories
SetupCollections(); // Creates Observable collections from List<T>
SetupCVSAndFilters(); // Creates CollectionViewSource for different categories
}
private void SetupCollections()
{
AStars = new ObservableCollection<IAEntity>(m_aStars);
BStars = new ObservableCollection<IAEntity>(m_bStars);
}
// Setup Collectionview source and Filters
// Get an exception:
// Additional information: Must create DependencySource on same Thread as the DependencyObject.
private void SetupCVSAndFilters()
{
AStarsCVS = new CollectionViewSource { Source = AStars };
BStarsCVS = new CollectionViewSource { Source = BStars };
AStarsCVS.View.Filter = FilterCompareData;
BStarsCVS.View.Filter = FilterCompareData;
}
The view/viewmodel works well without using Async/Task, but the fetches from the datasources are time-consuming(PInvokes) and I have multiple tabs which read different data and I'd appreciated pointers to help me understand where I'm going wrong with the usage of Task/Async

Every time you call Task.Run() a new thread is created. The exceptions you're getting have to do with your threads trying to access each other in ways they're not supposed to. I don't think I can see where it's going wrong just by the code you've provided, but I can tell you that in WPF, threads can access the objects of other threads using Dispatcher.BeginInvoke().
It also looks like you may not be getting the benefits of asynchronous programming that you're looking for. Having two await statements one after the other like that means the first task will complete before the second one even starts. I think you want Task.WaitAll().

Related

Concurrent Dictionary reading and removing items In parallel

Question background:
I am currently learning about concurrent operations using the.NET 6 Parallel.ForEachAsync loop.
I have created the following program which features two Tasks running parallel to each other in a Task.WhenAll function. The KeepAliveAsync method runs up to 3 degrees of parallelism on the HttpClient calls for a total list of ten items. The DisposeAsync method will remove these items from the concurrent dictionary but before this, the CleanItemBeforeDisposal method removes the property values of the Item object.
Code:
{
[TestClass]
public class DisposeTests
{
private ConcurrentDictionary<int, Item> items = new ConcurrentDictionary<int, Item>();
private bool keepAlive = true;
[TestMethod]
public async Task Test()
{
//Arrange
string uri = "https://website.com";
IEnumerable<int> itemsToAdd = Enumerable.Range(1, 10);
IEnumerable<int> itemsToDispose = Enumerable.Range(1, 10);
foreach (var itemToAdd in itemsToAdd)
{
items.TryAdd(itemToAdd, new Item { Uri = uri });
}
//Act
await Task.WhenAll(KeepAliveAsync(), DisposeAsync(itemsToDispose));
//Assert
Assert.IsTrue(items.Count == 0);
}
private async Task KeepAliveAsync()
{
HttpClient httpClient = new HttpClient();
do
{
ParallelOptions parallelOptions = new()
{
MaxDegreeOfParallelism = 3,
};
await Parallel.ForEachAsync(items.ToArray(), parallelOptions, async (item, token) =>
{
var response = await httpClient.GetStringAsync(item.Value.Uri);
item.Value.DataResponse = response;
item.Value.DataResponse.ToUpper();
});
} while (keepAlive == true);
}
private async Task DisposeAsync(IEnumerable<int> itemsToRemove)
{
var itemsToDisposeFiltered = items.ToList().FindAll(a => itemsToRemove.Contains(a.Key));
ParallelOptions parallelOptions = new()
{
MaxDegreeOfParallelism = 3,
};
await Parallel.ForEachAsync(itemsToDisposeFiltered.ToArray(), parallelOptions, async (itemsToDispose, token) =>
{
await Task.Delay(500);
CleanItemBeforeDisposal(itemsToDispose);
bool removed = items.TryRemove(itemsToDispose);
if (removed == true)
{
Debug.WriteLine($"DisposeAsync - Removed item {itemsToDispose.Key} from the list");
}
else
{
Debug.WriteLine($"DisposeAsync - Did not remove item {itemsToDispose.Key} from the list");
}
});
keepAlive = false;
}
private void CleanItemBeforeDisposal(KeyValuePair<int, Item> itemToDispose)
{
itemToDispose.Value.Uri = null;
itemToDispose.Value.DataResponse = null;
}
}
}
The Issue:
The code runs but I am running into an issue where the Uri property of the Item object is being set null from the CleanItemBeforeDisposal method as called from the Dispose method by design but then the HttpClient call is being made in the parallel KeepAliveAsync method at which point the shared Item object is null and errors with:
System.InvalidOperationException: An invalid request URI was provided. Either the request URI must be an absolute URI or BaseAddress must be set.
I have used the ToArray method on the shared ConcurrentDictionary as I believe this will create a snapshot of the dictionary at the time it is called but obviously this wont solve this race condition.
What is the correct way to go about handling a situation where two processes are accessing one shared list where there is the possibility one process has changed properties of an entity of that list which the other process requires?
I'll try to answer the question directly without getting into details about the design, etc.
ConcurrentDictionary is thread safe, meaning that multiple threads can safely add and remove items from the dictionary. That thread safety does not apply at all to whatever objects are stored as values in the dictionary.
If multiple threads have a reference to an instance of Item and are updating its properties, all sorts of unpredictable things can happen.
To directly answer the question:
What is the correct way to go about handling a situation where two processes are accessing one shared list where there is the possibility one process has changed properties of an entity of that list which the other process requires?
There is no correct way to handle that possibility. If you want the code to work in a predictable way you must eliminate that possibility.
It looks like you might have hoped that somehow the two operations will stay in sync. They won't. Even if they did, just once, it might never happen again. It's unpredictable.
If you actually need to set the Uri and Response to null, it's probably better to do that to each Item in the same thread right after you're done using those values. If you do those three things
Execute the request for a single Item
Do something with the values
Set them to null
...one after another in the same thread, it's impossible for them to happen out of order.
(But do you need to set them to null at all? It's not clear what that's for. If you just didn't do it, then there wouldn't be a problem to solve.)

adding items asynchronously to a list within an async method in C# .NET

I'm trying to better understand await and asynchronous operations in C# and .NET. I have an object objectA which among other things contains a list of errors. I also have a list of ids and I need to perform an async method on each id. If my async method catches an error, I want to add that to objectA field that contains the list of errors. My worry is that this might not be thread safe as multiple threads might be trying to modify the same object at the same time. I don't care about the order of the errors. Is some type of locking automatically handled? Should I just return the error from the method and then add that to the list at the end?
Task Main()
{
var objectA = new ExampleObject();
List<Task> getIdtasks = new List<Task>();
foreach (var id in ids)
{
tasks.Add(GetByIdAsync(id, objectA));
}
await Tasks.Whenall(getIdtasks)
}
private Task GetByIdAsync(object id, ExampleObject objectA)
{
try
{
var objectFromId = await DoSomethingAsync(id)
return objectFromId;
}
catch (Exception e)
{
objectA.errors.Add(e);
}
return null;
}
List is not Thread Safe. So if you try to do List.Add from different threads at hte same time it will fail.
You can use a lock for List.Add will solve the issue. OR
Use any Thread safe collections.

Is there a way to query a main-thread realm instance without blocking the main thread?

realm-dotnet
I'd like to pass some main-thread realm objects to some view-models, but don't want to block the UI while I retrieve them.
I need realm objects from a main-thread realm instance so that myRealmObject.PropertyChanged is called on the main thread. If no background query, is there a way to make a background-thread realm object's PropertyChanged be called on the main thread?
You can query on a background thread and create a ThreadSafeReference that you can pass to your VMs. For example:
var reference = await Task.Run(() =>
{
using (var realm = Realm.GetInstance())
{
var modelToPass = realm.All<MyModel>().Where(...).FirstOrDefault();
return ThreadSafeReference.Create(modelToPass);
}
});
// Pass reference to your ViewModel
Then in your ViewModel you can have
public void Initialize(ThreadSafeReference.Object<MyModel> reference)
{
var realm = Realm.GetInstance();
var myModel = realm.ResolveReference(reference);
// Do stuff with myModel - it's a main thread reference to
// the model you resolved on the background thread
}
Check out the docs for more detailed explanation.

c# simple background thread

I am looking for a simple way to do a task in background, then update something (on the main thread) when it completes. It's in a low level 'model' class so I can't call InvokeOnMainThread as I don't have an NSObject around. I have this method:
public void GetItemsAsync(Action<Item[]> handleResult)
{
Item[] items=null;
Task task=Task.Factory.StartNew(() =>
{
items=this.CreateItems(); // May take a second or two
});
task.ContinueWith(delegate
{
handleResult(items);
}, TaskScheduler.FromCurrentSynchronizationContext());
}
This seems to work OK, but
1) Is this the best (simplest) way?
2) I'm worried about the local variable:
Item{} items=null
What stops that disappearing when the method returns before the background thread completes?
Thanks.
I think your method slightly violates a Single Responsibility Principle, because it doing too much.
First of all I suggest to change CreateItems to return Task instead of wrapping it in the GetItemsAsync:
public Task<Item[]> CreateItems(CancellationToken token)
{
return Task.Factory.StartNew(() =>
// obtaining the data...
{});
}
CancellationToken is optional but can help you if you able to cancel this long running operation.
With this method you can remove GetItemsAsync entirely because its so simple to handle results by your client without passing this delegate:
// Somewhere in the client of your class
var task = yourClass.CreateItems(token);
task.ContinueWith(t =>
// Code of the delegate that previously
// passed to GetItemsAsync method
{}, TaskScheduler.FromCurrentSynchronizationContext());
Using this approach you'll get more clear code with only one responsibility. Task class itself is a perfect tool for representing asynchronous operation as a first class object. Using proposed technique you can easily mock you current implementation with a fake behavior for unit testing without changing your clients code.
Something like this:
public void GetItemsAsync(Action<Item[]> handleResult)
{
int Id = 11;
Task<Item[]> task = Task.Factory.StartNew(() => CreateItems(Id)); // May take a second or two
task.ContinueWith(t => handleResult(t.Result), TaskScheduler.FromCurrentSynchronizationContext());
}
Your code looks fine.
It's a perfect example of when to use async / await if you can use C# 5, but if not, you have to write it as you have done with continuations.
The items variable is captured in your lambdas, so that's fine too.
When you write a lambda that uses an outside variable, the C# compiler creates a class that contains the variable. This is called a closure and means you can access the variable inside your lambda.
Here is a nicer soultion:
public void GetItemsAsync(Action<Item[]> handleResult)
{
var task = Task.Factory.StartNew<Item[]>(() =>
{
return this.CreateItems(); // May take a second or two
});
task.ContinueWith(delegate
{
handleResult(task.Result);
}, TaskScheduler.FromCurrentSynchronizationContext());
}

asynchronous modification of ObservableCollections

I have problems, updating a WPF ListView Bound to a an ObservableCollection<T> within an Task Thread using (Task Parallel Library)
I have an Small Tool reading Edifact Files, and displaying an Count of each Segment (first three letters of a Line).
The contained Segments with their Counts are displayed in an Listbox.
Wenn I initially Load a File all works fine, and I see the GUI Counting up the Segments.
My Programm allowed switching to another File, If I do that (using exactly the same Code) it failes with the following Exception.
This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.
Here is the Code that fails
public class SegementLineCollection : ObservableCollection<SegmentLine>
{
public void IncrementCount(string Segment)
{
Segment = Segment.ToUpper();
SegmentLine line;
if (this.Select(x => x.SegmentName).Contains(Segment))
{
line = this.Where(x => x.SegmentName == Segment).FirstOrDefault();
}
else
{
line = new SegmentLine();
line.SegmentName = Segment;
this.Add(line); // <--- At this point comes the Exception
}
line.Count++;
}
}
Here is the TPL Call I use:
private string _FileName;
public string FileName
{
get
{
return _FileName;
}
set
{
_FileName = value;
OnPropertyChanged("FileName");
if (!String.IsNullOrEmpty(value))
new Task(() => StartFile()).Start();
}
}
Does anyone have any hit for me?
------------ E D I T ------------------
The provided Answers using TaskScheduler.FromCurrentSynchronizationContext() or Dispatcher did not do the Trick!
Is it possible that changing the Binding when loading the new does the trick.
Here is the Binding I use, the Reader Onject is Switched in the ViewModel, and the GUI is Notfied with INotifyPropertyChanged implementation
Use a dispatcher to access the collection:
if (Dispatcher.CurrentDispatcher.CheckAccess())
this.Add(...)
else
Dispatcher.CurrentDispatcher.Invoke(() => this.Add(...));
You need to call IncrementCount on the GUI thread.
With TPL you can use TaskScheduler.FromCurrentSynchroniztionContext() in your task or continuation.
var task = new Task<string>(() => { return "segment"; })
var task2 = task.ContinueWith(t => IncrementCount(t.Result),
TaskScheduler.FromCurrentSynchroniztionContext());
task.Start();
As you are acting on different thread you need to use Dispatcher.BeginInvoke to run an updates on a collection bound to UI
I have a solution for this kind of problems in the following blog..
http://bathinenivenkatesh.blogspot.co.uk/2011/07/wpf-build-more-responsive-ui.html
it got detailed explanation and code snippets...

Categories

Resources