I have the following code to connect to MYOB's SDK
var cfsCloud = new CompanyFileService(_configurationCloud, null, _oAuthKeyService);
cfsCloud.GetRange(OnComplete, OnError);
where
private void OnComplete(HttpStatusCode statusCode, CompanyFile[] companyFiles)
{ // ask for credentials etc }
I want to convert this to use a TaskCompletionSource
like this example
however my OnComplete has multiple parameters.
How do I code that?
As mentioned in the comment
The SDK for Accountright API supports async/await i.e. GetRangeAsync
so you can do something like this if you wanted/needed to wrap it in a TaskCompletionSource
static Task<CompanyFile[]> DoWork()
{
var tcs = new TaskCompletionSource<CompanyFile[]>();
Task.Run(async () =>
{
var cfsCloud = new CompanyFileService(_configurationCloud, null, _oAuthKeyService);
var files = await cfsCloud.GetRangeAsync();
tcs.SetResult(files);
});
return tcs.Task;
}
Related
I'm calling a third-party API which has a method that looks like this:
myServiceClient.Discover(key, OnCompletionCallback);
public bool OnCompletionCallback(string response)
{
// my code
}
My challenge is, I have to call Discover because it does some work under-the-covers that I need. At the same time, I have to wait for Discover to complete before running my custom code. To further complicate matters, I can't just put my code in the OnCompletionCallback handler because I need to call the code above from a Func delegate. In short, I want to do this:
Func<SystemTask> myFunction = async () =>
{
await myServiceClient.Discover(key);
// my code
}
However, I can't do this because the third-party API uses a callback approach instead of an async/await approach.
Is there some way to make the callback approach work in an async / await world?
If I understand you correctly you can do something like this
public Task<bool> MyAsyncFunction()
{
var tcs = new TaskCompletionSource<bool>();
myServiceClient.Discover("somekey", s => {
//........
var res = true;
tcs.TrySetResult(res);
return res;
});
return tcs.Task;
}
Now you can await MyAsyncFunction
I am using a network API which works with callbacks. So basically, I have a bunch of method calls that I need to use in this 3rd party library which look like this:
void SendNetworkRequest(string requestType, Action<Response> callback)
I find the code getting a little bit wacky because all of my methods which depend on network resources from this 3rd party API also need to implement callbacks themselves. For example, in my main scene I may want to get the players information and my code would look something like this:
void InitMainScene()
{
_networkHelper.SendNetworkRequest("GetPlayersInfo",OnPlayerInfoResponse);
}
void OnPlayerInfoResponse(Response response)
{
_playerInfo = response.Info;
}
I have recently gotten into RX and am using it abundently in my code. I have read read some about async/await. I have experimented quite a bit, especially with RX, and tried working with Observable.FromAsync() but could not get it to work..
What am I missing here? How can I write code which is cleaner and doesn't require the callback using RX or async/await? The following is the psuedocode which I'm looking for:
void InitMainScene()
{
_playerInfo = Overservable.DoMagicThing<Response>( () => {
_networkHelper.SendNetworkRequest("GetPlayersInfo",(r) =>{return r;}); });
}
Here's how to do it with Rx. I swapped out Response for string just so I could compile and test, but trivial to swap back.:
public IObservable<string> SendNetworkRequestObservable(string requestType)
{
return Observable.Create<string>(observer =>
{
SendNetworkRequest(requestType, s => observer.OnNext(s));
observer.OnCompleted();
return Disposable.Empty;
});
}
// Define other methods and classes here
public void SendNetworkRequest(string requestType, Action<string> callback)
{
callback(requestType); // implementation for testing purposes
}
I am not sure about RX. However, you can convert callback-based SendNetworkRequest to an awaitable task like this:
void SendNetworkRequest(string requestType, Action<Response> callback)
{
// This code by third party, this is here just for testing
if (callback != null)
{
var result = new Response();
callback(result);
}
}
Task<Response> SendNetworkRequestAsync(string requestType)
{
return Task.Run(() =>
{
var t = new TaskCompletionSource<Response>();
SendNetworkRequest(requestType, s => t.TrySetResult(s));
return t.Task;
});
}
Now you can consume SendNetworkRequestAsync with async/await more naturally
Here's my take on doing this with Rx.
Inside the NetworkHelper class add this method:
public IObservable<Response> SendNetworkRequestObservable(string requestType)
{
return
Observable
.Create<Response>(observer =>
{
Action<Response> callback = null;
var callbackQuery =
Observable
.FromEvent<Response>(h => callback += h, h => callback -= h)
.Take(1);
var subscription = callbackQuery.Subscribe(observer);
this.SendNetworkRequest(requestType, callback);
return subscription;
});
}
This creates an observable that internally uses Observable.FromEvent(...).Take(1) to create an observable based on expecting a single call to the Action<Response> callback passed to the SendNetworkRequest method.
The only issue with this is that the call occurs on the current thread right to completion. If you want this code to run on a background thread, but return the result to the current thread, then you can do this:
public IObservable<Response> SendNetworkRequestObservable(string requestType)
{
return
Observable
.Start(() =>
Observable
.Create<Response>(observer =>
{
Action<Response> callback = null;
var callbackQuery =
Observable
.FromEvent<Response>(h => callback += h, h => callback -= h)
.Take(1);
var subscription = callbackQuery.Subscribe(observer);
this.SendNetworkRequest(requestType, callback);
return subscription;
}))
.Merge();
}
Either way you'd call it like this:
var _playerInfo =
_networkHelper
.SendNetworkRequestObservable("GetPlayersInfo")
.Wait();
Just a side-note: your DoMagicThing is a synchronous call. In Rx that's a .Wait() call, but it's better to just use a normal .Subscribe when you can.
As per the comment, you could do this with async:
async void InitMainScene()
{
_playerInfo = await _networkHelper.SendNetworkRequestObservable("GetPlayersInfo");
}
I wonder if there is a code there that refresh my DataServiceCollection that is loaded in a WPF client using BeginExecute async await Task as show below:
public static async Task<IEnumerable<TResult>> ExecuteAsync<TResult>(this DataServiceQuery<TResult> query)
{
//Thread.Sleep(10000);
var queryTask = Task.Factory.FromAsync<IEnumerable<TResult>>(query.BeginExecute(null, null), (asResult) =>
{
var result = query.EndExecute(asResult).ToList();
return result;
});
return await queryTask;
}
I call the extension method as the following:
public async void LoadData()
{
_ctx = new TheContext(the URI);
_dataSource = new DataServiceCollection<TheEntity>(_ctx);
var query = _ctx.TheEntity;
var data = await Task.Run(async () => await query.ExecuteAsync());
_dataSource.Clear(true);
_dataSource.Load(data);
}
LoadData is Called in a ViewModel
Change a field value by hand using SQL Management Studio
Call LoadData again <<<< Does not refresh!!!
Meanwhile, If I use Load method the data is refreshed without any problems as:
var query = _ctx.TheEntity;
_dataSource.Load(query);
Another issue is that I do not know how to Cancel client changes. Last question is whether the MergeOption has any effect to BeginExecute..EndExecute or it only works with Load method?
I suspect that the data context does not like being accessed from multiple threads.
I recommend that you first remove all processing from your extension method:
public static Task<IEnumerable<TResult>> ExecuteAsync<TResult>(this DataServiceQuery<TResult> query)
{
return Task.Factory.FromAsync(query.BeginExecute, query.EndExecute, null);
}
Which you should use without jumping onto a background thread:
public async Task LoadDataAsync()
{
_ctx = new TheContext(the URI);
_dataSource = new DataServiceCollection<TheEntity>(_ctx);
var query = _ctx.TheEntity;
var data = await query.ExecuteAsync();
_dataSource.Clear(true);
_dataSource.Load(data);
}
My OperationContract:
public List<MessageDTO> GetMessages()
{
List<MessageDTO> messages = new List<MessageDTO>();
foreach (Message m in _context.Messages.ToList())
{
messages.Add(new MessageDTO()
{
MessageID = m.MessageID,
Content = m.Content,
Date = m.Date,
HasAttachments = m.HasAttachments,
MailingListID = (int)m.MailingListID,
SenderID = (int)m.SenderID,
Subject = m.Subject
});
}
return messages;
}
In Service Reference configuration I checked the option "Generate asynchronous operations". How do I use the generated GetMessagesAsync()? In the net I found examples that use AsyncCallback, however I'm not familiar with that. Is there a way to use it in some friendly way like async and await keywords in .NET 4.5? If not, what should I do to invoke the method asynchronously?
If you select 'Generate asynchrounous operations', you will get the 'old' behavior where you have to use callbacks.
If you want to use the new async/await syntax, you will have to select 'Generate task-based operations' (which is selected by default).
When using the default Wcf template, this will generate the following proxy code:
public System.Threading.Tasks.Task<string> GetDataAsync(int value) {
return base.Channel.GetDataAsync(value);
}
As you can see, there are no more callbacks. Instead a Task<T> is returned.
You can use this proxy in the following way:
public static async Task Foo()
{
using (ServiceReference1.Service1Client client = new ServiceReference1.Service1Client())
{
Task<string> t = client.GetDataAsync(1);
string result = await t;
}
}
You should mark the calling method with async and then use await when calling your service method.
Your Service Reference can (if you are using .Net 4.5) be set to generate task-based async calls. (Configure Service Reference > check Allow generation of asynchronous operations > select Generate task-based operations) These can be used like any async method. Here's an example of how to use it:
using (var proxy = new YourServiceClient())
{
var t1 = proxy.GetMessagesAsync();
var t2 = proxy.GetMessagesAsync();
//they're runnning asynchronously now!
//let's wait for the results:
Task.WaitAll(t1, t2);
var result1 = t1.Result;
var result2 = t2.Result;
Console.WriteLine(result1);
Console.WriteLine(result2);
}
If your client is not using .Net 4.5, you cannot generate service references that use async. You'll have to do it the old fashioned way, using callbacks. Here's an example:
static void m()
{
var proxy = new YourServiceClient();
proxy.GetMessagesCompleted += proxy_GetMessagesCompleted;
proxy.GetMessagesAsync();
}
static void proxy_GetMessagesCompleted(object sender, GetMessagesCompletedEventArgs e)
{
var proxy = (IDisposable)sender;
proxy.Dispose(); //actual code to close properly is more complex
if (e.Error != null)
{
// do something about this
}
var result = e.Result;
Console.WriteLine(result);
}
Note that in actual code for either of these scenarios, you shouldn't use using or IDisposable.Dispose() to clean up the client, see Avoiding Problems with the Using Statement and this code to get you started into the confusing world of closing these things.
If you're on VS2012, then you can use the *Async calls like this:
var proxy = new MyClient();
var result = await proxy.GetMessagesAsync();
How about something like this...
public async Task<string> DoSomething()
{
var someProxy = new ServiceClient();
var t = someProxy.SomeMethodAsync();
await Task.WhenAny(t);
return t.Result;
}
I have got the code that using WCF, requests data from the server.
For Example:
public static Company LoadCompanyInfo(Guid session)
{
var client = new QualerServiceClient("QualerService");
return client.GetCompanyInfo(session);
}
I need to make my wpf application to run these code asynchronously.
I try:
public static Company LoadCompanyInfoAsync(Guid session)
{
var client = new QualerServiceClient("QualerService");
client.BeginGetCompanyInfo(session, new AsyncCallback(EndLoadCompanyInfoAsync), client);
// How to Get Data from AsyncCallback function?
return null;
}
private static void EndLoadCompanyInfoAsync(IAsyncResult r)
{
var client = r.AsyncState as QualerServiceClient;
var result = client.EndGetCompanyInfo(r);
// how to return the result value ??
}
But I don't know how to return data from callback function.
I have got methods:
BeginGetCompanyInfo and EndGetCompanyInfo
GetCompanyInfoAsync
and event:
GetCompanyInfoCompleted.
Quastions:
How can I get the data from the callback method?
What is the difference between GetCompanyInfoAsync and Begin\End?
Best Practices: How can execute a method asynchronously, so that the GUI of my WPF App is not freezes?
I'm assuming that you're using VS2012.
First, if your target is .NET 4.0, then install (via NuGet) the Async Targeting pack. If your target is .NET 4.5, you don't have to do anything special.
Next, re-create your client proxy. The existing Begin/End/Async endpoints will be replaced by a single Task<Company> GetCompanyInfoAsync(Guid).
Now you can use it like this:
public static Task<Company> LoadCompanyInfoAsync(Guid session)
{
var client = new QualerServiceClient("QualerService");
return client.GetCompanyInfoAsync(session);
}
public async void ButtonClickOrWhatever(...)
{
var company = await LoadCompanyInfoAsync(mySession);
// Update UI with company info.
}
The old Begin/End method pair was using the Asynchronous Programming Model (APM). The old Async/event pair was using the Event-based Asynchronous Programming model (EAP). The new Async method is using the Task-based Asynchronous Programming model (TAP). I have more information on async WCF on my blog.
You could use the async CTP for .net 4, which will become part of .net in 5.
It adds two new keywords into C# the async and await keywords.
You mark a method or lamda with the async keyword and return a
Task or Task<T>
when you call your method you would call it like so
var result = await LoadCompanyInfo(sessionId);
Async CTP
public static async Task<Company> LoadCompanyInfo(Guid session)
{
Company result = default(Company);
await TaskEx.Run(()=>{
var client = new QualerServiceClient("QualerService");
result = client.GetCompanyInfo(session);
});
return result;
}
Use TPL:
Task t = TaskFactory.FromAsync(
beginMethod: BeginGetCompanyInfo,
endMethod: EndGetCompanyInfo,
arg1: session,
state: null);
t.ContinueWith(result =>
{
// handle result
});