I am building a screen for my app in xamarin.forms the caul is based on a tabbedpage which is built dynamically based on a list of objects which I get as a result of consuming a service.
After I call the method to consume the API that brings the list, I need to go through it based on certain data of it to fill an observable collection of viewmodels, which will be the tabs. The problem I have is that I do not know how to call the async method that consumes the API in a synchronized way so that the consumption of the API does not conflict with the operation of going through the list.
Then a fraction of the code of my ViewModel:
public MonitoringViewModel()
{
LoadThings();
Tabs = new ObservableCollection<MonitoringTabsViewModel>();
foreach (PcThing t in Things)
{
Tabs.Add(new MonitoringTabsViewModel(t.description));
}
}
private async void LoadThings()
{
Things = new List<PcThing>(await App.WebApiManager.GetCustomerThinksAsync());
}
What I get is that in xamarin live player the app after a few seconds go from the green signal to the red one without showing anything, and in the log of it I get this:
Target of GetEnumerator is null (NullReferenceException)
Since you are doing this in the constructor , I would try the following:
using System.Threading.Tasks;
The risk here is if you are not in control of the LoadThings completing, it can hang.
public MonitoringViewModel()
{
var task = Task.Run(async () => { await LoadThings();}
Task.WaitAll(task); //block and wait for task to complete
public async Task<List<PcThing>> LoadThings()
{
return await App.WebApiManager.GetCustomerThinksAsync();
}
And in your ViewModel
Things = LoadThings().GetAwaiter().GetResult();
Related
I'm currently trying to write a simple C#-WPF-Application that functions as a simple universal 'launcher'. For different applications we program.
It's purpose is to check the current software version of the 'real' software and if a new one is available it starts to copy the installer from a network share and runs the installer afterwards.
Then it starts the 'real' application and thats it.
The user Interface mainly consists of a startup window which shows the user the currently executed action (version check, copy, installation, startup, ...).
Now I create my view and my viewModel in the overridden StartUp method in App.cs
public override OnStartup(string[] args)
{
var viewModel = new StartViewModel();
var view = new StartView();
view.DataContext = viewModel;
view.Show();
// HERE the logic for the Launch starts
Task.Run(() => Launch.Run(args));
}
The problem is that if I don't go async here the Main Thread is blocked and I cannot update the UI. Therefore I got it working by using the Task.Run(...). This solves my problem of blocking the UI thread, but I have some problems/questions with this:
I cannot await the task, because that would block the UI again. Where to await it?
Is my concept of starting this workflow here ok in the first place?
Some update to clarify: After I show the UI to the user my logic starts to do heavy IO stuff. The possible calls I came up with are the following 3 variants:
view.Show();
// v1: completely blocks the UI, exceptions are caught
DoHeavyIOWork();
// v2: doesn't block the UI, but exceptions aren't caught
Task.Run(() => DoHeavyIOWork());
// v3: doesn't block the UI, exceptions are caught
await Task.Run(() => DoHeavyIOWork());
Currently I'm not at my work PC so i apologies for not giving you the original code. This is an on the fly created version.
I guess v1 and v2 are bad because of exceptions and the UI blocking.
I thought v3 didn't work when I tried it in my office. Now it seems to work in my local example. But I'm really not sure about v3. Because I'm using async void StartUp(...) there. Is it okay here?
I cannot await the task, because that would block the UI again. Where to await it?
await doesn't block the UI. Using await here is fine.
Is my concept of starting this workflow here ok in the first place?
I usually recommend that some UI is shown immediately when doing any asynchronous operation. Then when the async operation is complete, you can update/replace the UI.
Thanks for all the replys.
After reading all your comments and combining some of your answers I came up with the following example. It is working under all circumstances I tested.
Hopefully there is not to much wrong in your opinion.
Code behind from App.xaml.cs
public partial class App : Application
{
readonly StartViewModel viewModel = new StartViewModel();
protected override async void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var view = new StartWindow
{
DataContext = viewModel
};
view.Show(); // alternative: Run(view);
// alternative: instead of calling it here it is possible
// to move these calls as callback into the Loaded of the view.
await Task.Run(() => DoHeavyIOWork());
}
private string GenerateContent()
{
var content = new StringBuilder(1024 * 1024 * 100); // Just an example.
for (var i = 0; i < 1024 * 1024 * 2; i++)
content.Append("01234567890123456789012345678901234567890123456789");
return content.ToString();
}
private void DoHeavyIOWork()
{
var file = Path.GetTempFileName();
for (var i = 0; i < 20; i++)
{
File.WriteAllText(file, GenerateContent());
File.Delete(file);
Dispatcher.Invoke(() => viewModel.Info = $"Executed {i} times.");
}
}
}
Code in StartViewModel.cs
class StartViewModel : INotifyPropertyChanged
{
private string info;
public event PropertyChangedEventHandler PropertyChanged;
public string Info
{
get => info;
set
{
info = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I have a WPF (UI) app that retrieves data from a SQL database on init/load time.
To make the app's startup time fast (and not freeze due to loading the data) i tried doing a couple of things to load the data asynchronously, but with not much luck in terms of the UX i was expecting.
Basically the UI still freezes (even if it is just for a brief moment).
Here's what i've tried:
The method which the data loads is marked with async Task. I invoke this method like this
// Note that i use ConfigureAwait(true) because the data is being loaded from the UI.
LoadData().CongigureAwait(true);
Here is the method:
private async void LoadData()
{
try
{
using (var context = new DataModel.BusinessData())
{
var people= await context.People
.ToListAsync()
.ConfigureAwait(true);
foreach (var person in people)
{
this.People.Add(new PersonItem(person));
}
}
}
catch (Exception ex)
{
throw;
}
}
Now despite the asynchronous nature of this data retrieval, it is being handled back on the main thread (the UI thread). Again, this is being called from the UI (MVVM pattern), so i need this.
The end result is this:
I still see a brief moment of UI freezing during startup time.
Question:
How can data be retrieved AND be handled back by the UI thread WITHOUT
freezing the UI? Is this even possible when being handled back by the
UI thread?
It's only the actual call to ToListAsync that is asynchronous. The rest of your code executes on the UI thread.
If People contains a lot of items, rendering them may be slow.
You could try to assing the People property to a new collection instead of adding the items to the data-bound collection one by one:
private async Task LoadData()
{
using (var context = new DataModel.BusinessData())
{
var people = await context.People.ToListAsync();
this.People = await Task.Run(() => new ObservableCollection(people.Select(person => new PersonItem(person))));
}
}
Remember to implement INotifyPropertyChanged and raise the PropertyChanged event for the People property.
You could also try to execute all code on a background thread in case the implementation of ToListAsync actually blocks:
private async Task LoadData()
{
await Task.Run(() =>
{
using (var context = new DataModel.BusinessData())
{
this.People = new ObservableCollection(context.People.Select(person => new PersonItem(person)).ToArray());
}
});
}
If it's still slow, you need to either decrease the number of items to display or use a faster component to render the items.
I need to centralize the calls to my realtime streaming Firebase database within a class, and call those methods from another class. The method is intended to 'listen' to the node within Firebase and then post to the application log upon updates.
However, my present setup notes it being synchronous and upon application run, returns null. How can I make this setup work?
TestFirebaseActions actions streaming class, method:
String output;
public async Task<String> realtimeStreamTest()
{
var observable = firebase
.Child("driver_rate")
.AsObservable<DriverRate>()
.Subscribe(d => output = d.Object.driversRate);
return output;
}
Calling method and attempting to "listen" from another class:
public async void firebaseRealStreamTestMethod()
{
TestFirebaseActions testFirebase = new TestFirebaseActions();
Task<String> contentsTask = testFirebase.realtimeStreamTest();
// await! control returns to the caller and the task continues to run on another thread
string contents = await contentsTask;
Log.Verbose("firebaseRealStreamTestMethod()", contents);
}
How can I create and call to allow for asynchronous listening on this observable from within another class?
I have 30 sub companies and every one has implemented their web service (with different technologies).
I need to implement a web service to aggregate them, for example, all the sub company web services have a web method with name GetUserPoint(int nationalCode) and I need to implement my web service that will call all of them and collect all of the responses (for example sum of points).
This is my base class:
public abstract class BaseClass
{ // all same attributes and methods
public long GetPoint(int nationalCode);
}
For each of sub companies web services, I implement a class that inherits this base class and define its own GetPoint method.
public class Company1
{
//implement own GetPoint method (call a web service).
}
to
public class CompanyN
{
//implement own GetPoint method (call a web service).
}
so, this is my web method:
[WebMethod]
public long MyCollector(string nationalCode)
{
BaseClass[] Clients = new BaseClass[] { new Company1(),//... ,new Company1()}
long Result = 0;
foreach (var item in Clients)
{
long ResultTemp = item.GetPoint(nationalCode);
Result += ResultTemp;
}
return Result;
}
OK, it works but it's so slow, because every sub companys web service is hosted on different servers (on the internet).
I can use parallel programing like this:(is this called parallel programing!?)
foreach (var item in Clients)
{
Tasks.Add(Task.Run(() =>
{
Result.AddRange(item.GetPoint(MasterLogId, mobileNumber));
}
}
I think parallel programing (and threading) isn't good for this solution, because my solution is IO bound (not CPU intensive)!
Call every external web service is so slow, am i right? Many thread that are pending to get response!
I think async programming is the best way but I am new to async programming and parallel programing.
What is the best way? (parallel.foreach - async TAP - async APM - async EAP -threading)
Please write for me an example.
It's refreshing to see someone who has done their homework.
First things first, as of .NET 4 (and this is still very much the case today) TAP is the preferred technology for async workflow in .NET. Tasks are easily composable, and for you to parallelise your web service calls is a breeze if they provide true Task<T>-returning APIs. For now you have "faked" it with Task.Run, and for the time being this may very well suffice for your purposes. Sure, your thread pool threads will spend a lot of time blocking, but if the server load isn't very high you could very well get away with it even if it's not the ideal thing to do.
You just need to fix a potential race condition in your code (more on that towards the end).
If you want to follow the best practices though, you go with true TAP. If your APIs provide Task-returning methods out of the box, that's easy. If not, it's not game over as APM and EAP can easily be converted to TAP. MSDN reference: https://msdn.microsoft.com/en-us/library/hh873178(v=vs.110).aspx
I'll also include some conversion examples here.
APM (taken from another SO question):
MessageQueue does not provide a ReceiveAsync method, but we can get it to play ball via Task.Factory.FromAsync:
public static Task<Message> ReceiveAsync(this MessageQueue messageQueue)
{
return Task.Factory.FromAsync(messageQueue.BeginReceive(), messageQueue.EndPeek);
}
...
Message message = await messageQueue.ReceiveAsync().ConfigureAwait(false);
If your web service proxies have BeginXXX/EndXXX methods, this is the way to go.
EAP
Assume you have an old web service proxy derived from SoapHttpClientProtocol, with only event-based async methods. You can convert them to TAP as follows:
public Task<long> GetPointAsyncTask(this PointWebService webService, int nationalCode)
{
TaskCompletionSource<long> tcs = new TaskCompletionSource<long>();
webService.GetPointAsyncCompleted += (s, e) =>
{
if (e.Cancelled)
{
tcs.SetCanceled();
}
else if (e.Error != null)
{
tcs.SetException(e.Error);
}
else
{
tcs.SetResult(e.Result);
}
};
webService.GetPointAsync(nationalCode);
return tcs.Task;
}
...
using (PointWebService service = new PointWebService())
{
long point = await service.GetPointAsyncTask(123).ConfigureAwait(false);
}
Avoiding races when aggregating results
With regards to aggregating parallel results, your TAP loop code is almost right, but you need to avoid mutating shared state inside your Task bodies as they will likely execute in parallel. Shared state being Result in your case - which is some kind of collection. If this collection is not thread-safe (i.e. if it's a simple List<long>), then you have a race condition and you may get exceptions and/or dropped results on Add (I'm assuming AddRange in your code was a typo, but if not - the above still applies).
A simple async-friendly rewrite that fixes your race would look like this:
List<Task<long>> tasks = new List<Task<long>>();
foreach (BaseClass item in Clients) {
tasks.Add(item.GetPointAsync(MasterLogId, mobileNumber));
}
long[] results = await Task.WhenAll(tasks).ConfigureAwait(false);
If you decide to be lazy and stick with the Task.Run solution for now, the corrected version will look like this:
List<Task<long>> tasks = new List<Task<long>>();
foreach (BaseClass item in Clients)
{
Task<long> dodgyThreadPoolTask = Task.Run(
() => item.GetPoint(MasterLogId, mobileNumber)
);
tasks.Add(dodgyThreadPoolTask);
}
long[] results = await Task.WhenAll(tasks).ConfigureAwait(false);
You can create an async version of the GetPoint:
public abstract class BaseClass
{ // all same attributes and methods
public abstract long GetPoint(int nationalCode);
public async Task<long> GetPointAsync(int nationalCode)
{
return await GetPoint(nationalCode);
}
}
Then, collect the tasks for each client call. After that, execute all tasks using Task.WhenAll. This will execute them all in parallell. Also, as pointed out by Kirill, you can await the results of each task:
var tasks = Clients.Select(x => x.GetPointAsync(nationalCode));
long[] results = await Task.WhenAll(tasks);
If you do not want to make the aggregating method async, you can collect the results by calling .Result instead of awaiting, like so:
long[] results = Task.WhenAll(tasks).Result;
I'm trying to transition from the Event-based Asynchronous Pattern where I tracked running methods using unique id's and the asynoperationmanager. As this has now been dropped from Windows 8 Apps I'm trying to get a similar effect with Async/Await but can't quite figure out how.
What I'm trying to achieve is something like
private async Task updateSomething()
{
if(***the method is already running***)
{
runagain = true;
}
else
{
await someMethod();
if (runagain)
{
run the method again
}
}
}
The part I'm struggling with is finding out if the method is running. I've tried creating a Task and looking at the status of both that and the .status of the async method but they don't appear to be the correct place to look.
Thanks
UPDATE: This is the current code I use in .net 4 to achieve the same result. _updateMetaDataAsync is a class based on the Event-Based Asynchronous Pattern.
private void updateMetaData()
{
if (_updateMetaDataAsync.IsTaskRunning(_updateMetaDataGuid_CheckAllFiles))
{
_updateMetaDataGuid_CheckAllFiles_Again = true;
}
else
{
_updateMetaDataGuid_CheckAllFiles_Again = false;
_updateMetaDataAsync.UpdateMetaDataAsync(_updateMetaDataGuid_CheckAllFiles);
}
}
private void updateMetaDataCompleted(object sender, UpdateMetaDataCompletedEventArgs e)
{
if (_updateMetaDataGuid_CheckAllFiles_Again)
{
updateMetaData();
}
}
async/await itself is intended to be used to create sequential operations executed asynchronously from the UI thread. You can get it to do parallel operations, but generally the operations "join" back to the UI thread with some sort of result. (there's also the possibility of doing "fire-and-forget" types of asynchronous operations with await but it's not recommended). i.e. there's nothing inherent to async/await to support progress reporting.
You can get progress out of code using async/await; but you need to use new progress interfaces like IProgress<T>. For more info on progress reporting with async/await, see http://blogs.msdn.com/b/dotnet/archive/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis.aspx. Migrating to this should just be a matter of calling an IProgress delegate instead of a Progress event.
If you're using a Task you've created, you can check the Task's Status property (or just see Task.IsCompleted if completion is the only state you are interested in).
That being said, await will not "return" until the operation either completes, raises an exception, or cancels. You can basically safely assume that, if you're still waiting on the "await", your task hasn't completed.
SemaphoreSlim queueToAccessQueue = new SemaphoreSlim(1);
object queueLock = new object();
long queuedRequests = 0;
Task _loadingTask;
public void RetrieveItems() {
lock (queueLock) {
queuedRequests++;
if (queuedRequests == 1) { // 1 is the minimum size of the queue before another instance is queued
_loadingTask = _loadingTask?.ContinueWith(async () => {
RunTheMethodAgain();
await queueToAccessQueue.WaitAsync();
queuedRequests = 0; // indicates that the queue has been cleared;
queueToAccessQueue.Release()
}) ?? Task.Run(async () => {
RunTheMethodAgain();
await queueToAccessQueue.WaitAsync();
queuedRequests = 0; // indicates that the queue has been cleared;
queueToAccessQueue.Release();
});
}
}
}
public void RunTheMethodAgain() {
** run the method again **
}
The added bonus is that you can see how many items are sitting in the queue!