I'm attempting to write a c# wrapper around a third-party library written in native code for consumption in our apps, which are almost exclusively written in .NET, and I'm trying to remain faithful to the C# patterns. Almost all the calls in this library are asynchronous in nature, and it would seem appropriate to wrap all my async calls into Task<T> objects. Here's an oversimplified example of how the native library is structured:
delegate void MyCallback(string outputData);
class MyNativeLibrary
{
public int RegisterCallback(MyCallback callback); // returns -1 on error
public int RequestData(string inputData); // returns -1 on error
}
Right now, I've provided my return values through event subscription, however I believe this would be a far better way to return my data:
class WrapperAroundNativeCode
{
public async Task<string> RequestData(string inputData);
}
So far I've been unsuccessful in finding an appropriate way to implement this, and I'm reaching out to folks with more experience in working with Task<T> objects and the async/await pattern than I do.
You would use a TaskCompletionSource<TResult> for this. Something along the lines of the following code:
class WrapperAroundNativeCode
{
public async Task<string> RequestData(string inputData)
{
var completionSource = new TaskCompletionSource<string>();
var result = Native.RegisterCallback(s => completionSource.SetResult(s));
if(result == -1)
{
completionSource.SetException(new SomeException("Failed to set callback"));
return completionSource.Task;
}
result = Native.RequestData(inputData);
if(result == -1)
completionSource.SetException(new SomeException("Failed to request data"));
return completionSource.Task;
}
}
This answer assumes that there won't be concurrent calls to this method. If there were you would need some way to differentiate between the different calls. Many APIs provide a userData payload that you can set to a unique value per call, so that you can differentiate.
It sounds like you're looking for TaskCompletionSource<T>. You'd wrap your library by creating a TaskCompletionSource, creating an instance of MyNativeLibrary and registering a callback which set the result of the task completion source, and then requesting data from same instance. If either of these steps fails, set an error on the task completion source. Then just return the value of the TaskCompletionSource<>.Task property to the caller.
(This is assuming you can create separate instances of MyNativeLibrary - if you can only create a single instance across your whole app, it gets a lot harder.)
Related
Quick background. Used Flurl inside a class library I built to simplify my code for communicating with a cloud storage api. Works beautifully when calling the library from a console app used to test all of the methods. When attempting to use the exact same class library with a simple winform, the same method that returns very quickly using the console app now seems to never return a result. When debugging, the code below gets to the ".GetAsync()" line and then never returns a result and also prevents the debug session from continuing. No error message is ever thrown.
I found a comment on the Flurl site that someone seemed to be having this same issue but, it doesn't seem like they posted the question here as was recommended. Anything that could point me in the right direction would be greatly appreciated.
Flurl code wrapped in async method
public async Task<AccountInfo> Authorize()
{
string credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes(Utils.ToNonSecureString(accountId) + ":" + Utils.ToNonSecureString(applicationKey)));
var result = await B2UrlType.Authorize
.WithHeader("Authorization", "Basic " + credentials)
.GetAsync()
.ReceiveJson<AccountInfo>();
return result;
}
Console app calling code that works perfectly
if (client == null)
{
var vault = new Vault();
Console.WriteLine("Retrieving account keys");
client = new Client(vault.GetAccountId(), vault.GetApiKey());
Console.WriteLine("Successfully retrieved account keys");
Console.WriteLine("Created new client");
client.Authorize().GetAwaiter().GetResult();
}
Winform calling code that does not return
private Client client;
public MainWindow()
{
InitializeComponent();
var vault = new Vault();
client = new Client(vault.GetAccountId(), vault.GetApiKey());
client.Authorize().GetAwaiter().GetResult();
}
Your original code hangs because you're blocking the UI thread with your call to GetResult(). This is not a Flurl-specific problem; this is async 101.
Your fix works because you're no longer blocking, but you're also not awaiting your call to Auth(), which is really the equivalent of just calling client.Authorize() without await or GetResult() directly from your MainWindow() constructor. You're no longer blocking, but you are fire-and-forgetting, meaning any exceptions that might occur in client.Authorize will go unobserved, causing bugs that are hard to track down.
Rule of thumb: Like with any async library, call Flurl's async methods from other async methods and await them whenever possible. In console apps, you have to block the main thread somewhere or the application will simply exit before tasks are complete. (I like to do it at the very top - define a MainAsync method containing all the work and call MainAsync().Wait() from Main.) But with WinForms apps, there's a good place to put async code where you never have to block or fire-and-forget: Event handlers.
I haven't done WinForms in a long while, but based on other answers it seems like a decent event for initialization code is Window.Load. Moving your authorization call there would be a good solution. Something like this:
private async void MainWindow_Load(object sender, System.EventArgs e)
{
await client.Authorize();
}
This is what worked but I'm still not sure why...
private Client client;
public MainWindow()
{
InitializeComponent();
var vault = new Vault();
client = new Client(vault.GetAccountId(), vault.GetApiKey());
Auth();
}
private async void Auth()
{
await client.Authorize();
}
Wrapping the authorization call in an async method allowed the httpPost to complete and return results.
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.
I wanted to create a simple IsEmpty(StorageFolder directory) method that works in WinRT apps, i.e. that uses the async API. I don't see a relevant method in the documentation for the StorageFolder class, and I also haven't found anything in my searches. I'm sorry if I've missed it!
I managed to create the following method, which works:
public static async Task<bool> IsEmpty(StorageFolder directory)
{
var files = await directory.GetFilesAsync();
if (files.Count > 0)
{
return false;
}
var folders = await directory.GetFoldersAsync();
if (folders.Count > 0)
{
return false;
}
return true;
}
But... is there a cleaner way? Either native or that I could code... It should be a simple thing to check if a directory is empty, but I know I've faced problems before when simply trying to check if a directory or file exists using the async API of WinRT.
I'm also not entirely sure if the asynchronous calls to GetFilesAsync and GetFoldersAsync get every file/folder in the directory before returning, or if they can somehow only get a single item before realizing that Count will be higher than 0 (I'm thinking of lazy evaluation, like in the Haskell language, but this is C#...). If they could, I would be more at peace with this method :)
StorageFolder.GetItemsAsync(0,1) will retrieve the first file or sub-folder:
public static async Task<bool> IsEmpty(StorageFolder directory)
{
var items = await directory.GetItemsAsync(0,1);
return items.Count == 0;
}
GetFilesAsync and GetFoldersAsync will return all of the files or folders. These calls don't know that you are only going to care about the count, and I doubt the compiler is smart enough to realize that and rewrite the calls to use filtered versions automatically.
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?
Recently, I successfully created a long-polling service using HttpAsyncHandler’s. During the development it came to me (that) I “might” be able to re-use the AsyncResult object many times without long-polling repeatedly. If possible, I could then “simulate” push-technology by re-building or re-using the AsyncResult somehow (treating the first request as though it were a subscription-request).
Of course, the first call works great, but subsequent calls keep giving me “Object not set to an instance of an object”. I am “guessing” it is because certain objects are static, and therefore, once "completed" cannot be reused or retrieved (any insight there would be AWESOME!).
So the question is…
Is it possible to build dynamically a new callback from the old callback?
The initial "subscription" process goes like this:
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
Guid id = new Guid(context.Request["Key"]);
AsyncResult request = new AsyncResult(cb, context, id);
Service.Singleton.Subscribe(request);
return request;
}
Here is an example of what the service does:
private void MainLoop()
{
while (true)
{
if (_subscribers.Count == 0)
{
if (_messages.Count == max)
_messages.Clear();
}
else
{
if (_messages.Count > 0)
{
Message message = _messages.Dequeue();
foreach (AsyncResult request in _subscribers.ToArray())
{
if(request.ProcessRequest(message));
_subscribers.Remove(request);
}
}
}
Thread.Sleep(500);
}
}
Here is an example of what the AsyncResult.ProcessRequest() call does:
public bool ProcessRequest(Message message)
{
try
{
this.Response = DoSomethingUseful(message);
this.Response.SessionValid = true;
}
catch (Exception ex)
{
this.Response = new Response();
this.Response.SessionValid = false;
}
this.IsCompleted = true;
_asyncCallback(this);
return this.IsCompleted;
}
SO...WOULD SOMETHING LIKE THIS BE POSSIBLE?
I literally tried this and it didn't work...but is SOMETHING "like" it possible?
AsyncResult newRequest = new AsyncResult(request.cb, request.context, request.id);
if(request.ProcessRequest(message))
{
_subscribers.Remove(request);
Subscribers.Add(newRequest);
}
IAsyncResult implementations must satisfy certain invariants, one of which is that it can only be completed once. You don't identify the AsyncResult you're using, but if it's Richter's famous version, then it would uphold that invariant.
If you don't want to go through the trouble of implementing the event-based asynchronous pattern, then the best option is Microsoft Rx, which is a true push-based system.
Let me first preface by saying I am completely unfamiliar with IHttpAsyncHandler interface and usage.
That being said, in general when using an asynchronous programming model, each AsyncResult represents a specific asynchronous method call and should not be reused. Seems like you are looking more for a RegisterEvent(callback) method than a BeginProcessing(callback method) - so even if you were able to get this to work, the design does not hold by asynchonous programming best practices (IMHO).
I assume that since you are using http which is request/response based, it seems unlikely that you will be able to push multiple responses for one request and even if you were able to somehow hack this up, your client will eventually get a timeout due to its unanswered request which would be problematic for what you are going for.
I know in Remoting you can register for remote events and WCF supports duplex contracts which can enable "push technology" if this is an option for you.
Good luck.