I am developing a windows app. There is a model class with a property CoverSource of type ImageSource and a property FileName of type string. Both of them are data bidden to views so I can't make their getters or setters async functions. Now I want that in the setter of FileName, an async function will be called and after that the CoverSource will be set asynchronously by reading file from ApplicationData.Current.LocalFolder. I am a newbie to C# and get little help from Google. So how to write such a async function and call it with callback, just something like that in Javascript?
You have a couple of conflicting requirements here. First, the Windows platform (just like any other modern mobile platform) requires your view updates to be synchronous. Blocking the UI thread is not considered acceptable; any updates must be immediate. Secondly, reading a file is an I/O-based operation, and thus naturally asynchronous. So that forces you into a non-blocking approach.
The answer is to realize that both requirements are correct. The UI must be updated immediately, and file I/O must take an arbitrary amount of time. So, the proper design is to think about what your UI looks like while the I/O is in progress, and then purposefully design for that scenario.
Something like this should work:
public string FileName
{
get { return fileName; }
set
{
fileName = value;
LoadFileAsync();
}
}
private async Task LoadFileAsync()
{
try
{
CoverSource = ...; // "Loading" image or something.
var image = await ...;
CoverSource = image;
}
catch (Exception ex)
{
CoverSource = ...; // "Error" image or something.
}
}
I have an MSDN article on async data binding that goes into much more detail, and introduces a NotifyTaskCompletion type that takes care of a lot of the boilerplate code with this approach.
Related
I have a number of web posts inside my application that need to send text data to a server but other than awaiting completion of the post shouldnt hold up the methods that are called from (large data posts that would slowdown logic etc that shouldnt be).
Currently im discarding the task as that appeared to be the correct method however on the server end logs indicate it seams to be closing the connection before the data is successfuly sent meaning I'm loosing most of the data in transit.
private void DoSomethingandPost()
{
BeforeMethod();
PushWebDataAsync(TheData1);
PushWebDataAsync(TheData2);
AfterMethod();
}
public static async void PushWebDataAsync(string Data)
{
...makes changes to the data...
try
{
_ = pushDataAync(Data);
}
catch (Exception e)
{
_ = pushDataAync(Data);
}
}
public System.Threading.Tasks.Task<System.Xml.XmlNode> pushDataAync(string Data)
{
return base.Channel.pushDataAync(Data);
}
My gut feeling is that if "AfterMethod" returns before the data has completed sending the connection to the server is cut and so the data isnt fully transmitted.
What Im trying to acheieve really is DoSomethingandPost() completes and exits but the two async Post's continue on their own until complete then exit.
If AfterMethod must run after the two PushWebDataAsync calls, then make the later return a Task, make AfterMethod async and await the push-methods. DoSomethingandPost will return at the first await-statement, doing the rest of the work at some later time . If you want to do the push concurrently then do
var task1 = PushWebDataAsync(TheData1);
var task2 = PushWebDataAsync(TheData2);
await Task.WhenAll(new []{task1, task2});
...
It is good practice to avoid async void since this makes it impossible for the caller to know if the call succeeded or not. If you know this will never be needed, like in the event handler for a button, then it is good practice to handle any exception that may be thrown.
I have a synchronous method that calls a method which collates a bunch of data on a custom object and stores it on a table entry on a Firebird database, located on a server.
On the server, a monitoring process keeps watching the first table for new entries using a database event (a table trigger raises an event which is captured by the monitor). When this event is raised, that data is sent to a third-party black-box service to be processed with the use of a proprietary library, that takes between near-naught and 1 minute to reply.
The third-party service replies with some data which is entered on a second table on the database. This second table has another trigger that the client's program monitors. The client's program must either wait until the third-party replies some data, or it times out (the same 1 minute).
I'm currently delving into the world of database events and I've reached an impasse:
Currently I have a key press that runs a synchronous method, which according to an application setting either runs another synchronous method, which runs flawlessly, or another method that inserts an entry on a Firebird database. This database is monitored by another process, which reads that entry, do some stuff, and inserts the new data on another table.
Back on the main program, what I currently have is the method has an event handler which is triggered when the new data is inserted. However, as it is an event, the rest of the method runs its course, ending prematurely, before the event handler has the chance to read the new data.
In pseudo code:
MainWindow_KeyDown(object sender, EventArgs e)
{
if (e.Key == X)
{
MakeADecision()
}
}
MakeADecision()
{
if (Properties.Settings.Default.MySetting) Console.Write(DoLocalStuff());
else Console.Write(DoRemoteStuff());
}
string DoRemoteStuff()
{
using (OldDataTableAdapter)
using (NewDataTableAdapter)
{
OldDataTableAdapter.Insert(OldData);
var revent = new FBRemoteEvent(MyConnectionString);
revent.RemoteEventCounts += (sender, e) =>
{
NewDataTableAdapter.Fill(NewDataDataTable);
NewData = NewDataDataTable[0].MYCOLUMN;
};
revent.QueueEvents("MY_FB_EVENT");
}
return NewData;
}
As you can see, the issue here is that DoRemoteStuff reaches its return before the event can be triggered. I tried turning DoRemoteStuff() into an async method, but I don't know how to use events with async methods. Can anyone please help me with this? Any tips or hints on how to work with async methods?
A possible solution would be to use a TaskCompletionSource so you can convert your method to an async method. This is based on Is it possible to await an event instead of another async method?.
MakeADecision()
{
if (Properties.Settings.Default.MySetting)
{
Console.Write(DoLocalStuff());
}
else
{
// Consider making MakeADecision async as well
NewData = DoRemoteStuff().Result;
Console.Write(NewData);
}
}
async Task<string> DoRemoteStuff()
{
Task<string> task;
using (OldDataTableAdapter)
{
OldDataTableAdapter.Insert(OldData);
task = WaitForEvent(MyConnectionString);
}
return await task;
}
private async Task<string> WaitForEvent(string connectionString)
{
var taskCompletionSource = new TaskCompletionSource<string>();
var revent = new FbRemoteEvent(connectionString);
revent.RemoteEventCounts += (sender, e) =>
{
using (NewDataTableAdapter)
{
NewDataTableAdapter.Fill(NewDataDataTable);
string newData = NewDataDataTable[0].MYCOLUMN;
taskCompletionSource.SetResult(newData);
}
sender.Dispose();
};
revent.QueueEvents("MY_FB_EVENT");
return await taskCompletionSource.Task;
}
Some things to point out:
You need to explicitly dispose the event to avoid a memory leak
The using for NewDataTableAdapter belongs within the event handler
The MakeADecision method seems like a candidate to be made async as well
A word of warning, my C# is a bit rusty (and I have never done much with async), so I'm not sure if this is the idiomatic way of doing it. I also did not test the code as written above (I wrote and tested a simpler version, but I may have introduced bugs while transforming your code to a similar solution).
This solution may also have the possibility of a race condition between inserting the new data triggering the event and registering for the event (unless the Dispose at the end of the using block is what commits the data), consider moving the WaitForEvent before inserting. Also consider the possibility of receiving the event from an update done for/by another change.
I'm working on an Xamarin.Forms application with MVVM. In my viewmodel I want to Get all the patients from an API. This is the code:
public async Task GetAllPatients()
{
try
{
isFetchingData = true;
var response = await httpClient.GetStringAsync(baseUrl + "/patient?query=ma");
var resultPatients =
JsonConvert.DeserializeObject<ObservableRangeCollection<PatientViewModel>>
(testJson,jsonSerializerSettings);
AllPatients.ReplaceRange(resultPatients);
Patients.ReplaceRange(resultPatients);
}
catch(Exception e)
{
Console.WriteLine("*****ERROR kon API niet ophalen");
Console.WriteLine(e.Message);
}
finally
{
CheckIfEmptyList();
isFetchingData = false;
}
}
At first I just got the API hard coded from a json string (testJson) in my code and everything went smoothly. But from the moment I put the htppClient out of commentary something strange happens (even when I don't use the variable as you can see in the code(I get the same result when I do use the variable though)).
The finally block is not executed.
It is to say, when I go and debug the app, the code goes through the finally and checks if the list is empty and puts isFetchingData to false. But I don't see that happening on the screen. If the list is empty a label should occur but now that label doesn't go away when list is not empty. The INotifyPropertyChanged does work good because without the httpClient it runs smoothly.
I'm very new to asynchronous programming so maybe I forgot to implement something that has to make sure the GetStringAsync ends properly? Maybe it keeps fetching the data and that is why I never see the finally block executed (even though it does behind the screen). I've read some articles about asynchronous programming but couldn't find something that could help me out.
I must also say that this method is called from the constructor, which makes it a little bit harder to run async. I tried calling it without async await and I tried calling it this way:
Task.Run(async ()=> { await GetAllPatients(); });
I tried with and without ConfigureAwait(false) but that doesn't make a difference either.
Finding a way to not put the method in the constructor (as suggested by CrowCoder and Albert) seemed the only possibility. In this case I managed to do so, but for other people it may not be always possible.
Because I work with MVVM without a framework and I'm very new to Xamarin and asynchronous programming (I'm a student), it was too difficult for me to find an alternative to the constructor.
I will put this as an answer, but if someone can give a code example where it would work to put the method in the constructor, or a workaround, it is still very welcome.
I am fairly new to Rx and am having trouble finding a solution to my problem. I am using Rx to commence a download through a client library. Currently it looks like:
private void DownloadStuff(string descriptor, Action<Stuff> stuffAction)
{
this.stuffDownloader.GetStuffObservable(descriptor).Subscribe(x => stuffAction(x))
}
Where stuffDownloader is a wrapper around download logic defined in the client library. But I encountered a problem where I call DownloadStuff too much, causing many downloads, and overwhelming the system. Now what I would like to do is
private void DownloadStuff(string descriptor, Action<Stuff> stuffAction)
{
this.stuffDownloader.GetStuffObservable(descriptor)
.SlowSubscribe(TimeSpan.FromMilliSeconds(50))
.Subscribe(x => stuffAction(x))
}
Where SlowSubscribe is some combination of Rx actions to only subscribe on some interval.
Normally I would just put these DownloadStuff calls on a queue and pull them off on an interval, but I've been trying to do more through Rx lately. Three solutions occur to me:
This functionality exists and can be done all on the subscription side.
This is possible but the infrastructure of the downloader is incorrect and should change (i.e. stuffDownloader needs to behave differently)
This shouldn't be done with Rx, do it another way.
It occurs to me #2 is possible by passing an IObservable of descriptors to the client library and somehow slowing how the descriptors get onto the Observable.
You could in theory use Rx to treat your requests as events. This way you could leverage the serializing nature of Rx to queue up downloads.
I would think that you network layer (or stuffDownloader) would do this for you, but if you want to join me for a hack....this is what I have come up with (Yeehaw!!)
1.
Dont pass an Action, use Rx!! You are basically losing the error handling here and setting yourself up for weird unhandled exceptions.
private void DownloadStuff(string descriptor, Action<Stuff> stuffAction)
becomes
private IObservable<Stuff> DownloadStuff(string descriptor)
2.
Now we just have one method calling another. Seems pointless. Throw away the abstraction.
3.
Fix the underlying. To me the stuffDownloader is not doing it's job. Update the interface to take an IScheduler. Now you can pass it a dedicated EventLoopScheduler to enforce the serialization of the work
public IObservable<Stuff> GetStuffObservable(string descriptor, IScheduler scheduler)
4.
Fix the implementation?
As you want to serialize your requests (hmmmm....) you can just make the call synchronous.
private Stuff GetSync(string description)
{
var request = (HttpWebRequest)WebRequest.Create("http://se300328:90/");
var response =request.GetResponse();
var stuff = MapToStuff(response);
return stuff;
}
Now you just call that in you other method
public IObservable<Stuff> GetStuffObservable(string descriptor, ISchedulerLongRunning scheduler)
{
return Observable.Create<Stuff>(o=>
{
try
{
var stuff = GetStuff(description);
o.OnNext(stuff);
o.OnCompleted();
}
catch(Exception ex)
{
o.OnError(ex);
}
return Disposable.Empty(); //If you want to be sync, you cant cancel!
})
.SubscribeOn(scheduler);
}
However, having done all of this, I am sure this is not what you really want. I would expect that there is a problem somewhere else in the system.
Another alternative is to consider using the Merge operator and it's max concurent feature?
I've just "earned" the privilege to maintain a legacy library coded in C# at my current work.
This dll:
Exposes methods for a big legacy system made with Uniface, that has no choice but calling COM objects.
Serves as a link between this legacy system, and another system's API.
Uses WinForm for its UI in some cases.
More visually, as I understand the components :
*[Big legacy system in Uniface]* ==[COM]==> [C# Library] ==[Managed API]==> *[Big EDM Management System]*
The question is: One of the methods in this C# Library takes too long to run and I "should" make it asynchronous!
I'm used to C#, but not to COM at all. I've already done concurrent programming, but COM seems to add a lot of complexity to it and all my trials so far end in either:
A crash with no error message at all
My Dll only partially working (displaying only part of its UI, and then closing), and still not giving me any error at all
I'm out of ideas and resources about how to handle threads within a COM dll, and I would appreciate any hint or help.
So far, the biggest part of the code I've changed to make my method asynchronous :
// my public method called by the external system
public int ComparedSearch(string application, out string errMsg) {
errMsg = "";
try {
Action<string> asyncOp = AsyncComparedSearch;
asyncOp.BeginInvoke(application, null, null);
} catch (ex) {
// ...
}
return 0;
}
private int AsyncComparedSearch(string application) {
// my actual method doing the work, that was the called method before
}
Any hint or useful resource would be appreciated.
Thank you.
UPDATE 1:
Following answers and clues below (especially about the SynchronizationContext, and with the help of this example) I was able to refactor my code and making it to work, but only when called from another Window application in C#, and not through COM.
The legacy system encounters a quite obscure error when I call the function and doesn't give any details about the crash.
UPDATE 2:
Latest updates in my trials: I managed to make the multithreading work when the calls are made from a test project, and not from the Uniface system.
After multiple trials, we tend to think that our legacy system doesn't support well multithreading in its current config. But that's not the point of the question any more :)
Here is a exerpt of the code that seems to work:
string application;
SynchronizationContext context;
// my public method called by the external system
public int ComparedSearch(string application, out string errMsg) {
this.application = application;
context = WindowsFormsSynchronizationContext.Current;
Thread t = new Thread(new ThreadStart(AsyncComparedSearchAndShowDocs));
t.Start();
errMsg = "";
return 0;
}
private void AsyncComparedSearch() {
// ANY WORK THAT AS NOTHING TO DO WITH UI
context.Send(new SendOrPostCallback(
delegate(object state)
{
// METHODS THAT MANAGE UI SOMEHOW
}
), null);
}
We are now considering other solutions than modifying this COM assembly, like encapsulating this library in a Windows Service and creating an interface between the system and the service. It should be more sustainable..
It is hard to tell without knowing more details, but there are few issues here.
You execute the delegate on another thread via BeginInvoke but you don't wait for it. Your try\catch block won't catch anything as it has already passed while the remote call is still being executed. Instead, you should put try\catch block inside AsyncComparedSearch.
As you don't wait for the end of the execution of remote method (EndInvoke or via callback) I am not sure how do you handle the results of the COM call. I guess then that you update the GUI from within AsyncComparedSearch. If so, it is wrong, as it is running on another thread and you should never update GUI from anywhere but the GUI thread - it will most likely result with a crash or other unexpected behavior. Therefore, you need to sync the GUI update work to GUI thread. In WinForms you need to use Control.BeginInvoke (don't confuse it with Delegate.BeginInvoke) or some other way (e.g. SynchronizationContext) to sync the code to GUI thread. I use something similar to this:
private delegate void ExecuteActionHandler(Action action);
public static void ExecuteOnUiThread(this Form form, Action action)
{
if (form.InvokeRequired) { // we are not on UI thread
// Invoke or BeginInvoke, depending on what you need
form.Invoke(new ExecuteActionHandler(ExecuteOnUiThread), action);
}
else { // we are on UI thread so just execute the action
action();
}
}
then I call it like this from any thread:
theForm.ExecuteOnUiThread( () => theForm.SomeMethodWhichUpdatesControls() );
Besides, read this answer for some caveats.