Passing callback Action to a method - c#

It is an asynchronous method that goes and queries the database and sometime returns.
Currently It is something like this:
this.Model.GetStudentKeys(keysList);
this.Foo1();
this.Foo2();
I am not proficient with the syntax but what I need to have is to change the code above in a way that those two Foo methods get called after the Async query is done. So I kind of know I need something like this:
this.Model.GetStudentKeys(keysList, callBack);
Action<object> callback {
this.Foo1();
this.Foo2();
}
But like I said I am not familiar with the syntax. Can you please help me with it?

You can use callbacks, but they are being superseded by C#'s async/await syntax. Before I explain said syntax however, I'll show you how you can achieve the same with Actions:
/*Action is a reference type, so its assignment is no different. Its value is a lambda
expression (http://msdn.microsoft.com/en-us/library/bb397687.aspx),
which you can think of as an anonymous method.*/
private Action<object> callback = o =>
{
object result = o;
this.Foo1();
this.Foo2();
};
/*It's good practice in C# to append 'Async' to the name of an asynchronous method
in case a synchronous version is implemented later.*/
this.Model.GetStudentKeysAsync(keysList, callback);
With this approach, your GetStudentsAsync method must invoke the callback, like so:
public void GetStudentsAsync(List<string> keys, Action<object> callback)
{
var returnValue = null;
//do some stuff...
callback(returnValue);
}
You can also pass the lambda expression directly as a parameter:
this.Model.GetStudentKeysAsync(keysList, o =>
{
this.Foo1();
this.Foo2();
});
With .NET 4.5 (and .NET 4.0 using the Microsoft Async package) however, you can use the async and await keywords provided by C# 5:
//notice the 'async' keyword and generic 'Task(T)' return type
public async Task<IEnumerable<StudentModel>> GetStudentsAsync(IEnumerable<int> keyList)
{
/*to invoke async methods, precede its invocation with
the 'await' keyword. This allows program flow to return
the line on which the GetStudentsAsync method was called.
The flow will return to GetStudentsAsync once the database
operation is complete.*/
IEnumerable<StudentModel> students = await FakeDatabaseService.ExecuteSomethingAsync();
return students;
}
Simply invoke the above method as follows:
IEnumerable<StudentModel> students = await this.Model.GetStudentsAsync(keyList);

You can define your function like that
void GetStudentKeys(keysList, Func<bool> callback)
{
Thread th = new Thread(delegate(){
//do some work in separate thread and inside that thread call
if(callback != null)
callback();
});
th.Start();
}
and change the function call to
this.Model.GetStudentKeys(keysList, () => {
this.Foo1();
this.Foo2();
return true;
});
Something like that.

Assuming the GetStudentKeys method returns a Task object (due to it being async), you could use the (new in C# 5.0) construct async and await,
public async void MethodName()
{
await this.Model.GetStudentKeysAsync(keysList); // <-- Async operation
this.Foo1();
this.Foo2();
}
Once the async operation completes, the code will then continue after the await keyword.

Related

c#-WPF: Cannot convert async lambda expression to delegate type 'Func<int>'

Im trying to understand how Lambda expression work with async methods.
I have a function
private int Server_Get_Int(){
Task<int> task = Task.Factory.StartNew<int>( async () => {
FirebaseClient c = Server_Connect();
FirebaseResponse response = await c.GetAsync("todos/set");
return response.ResultAs<int>(); //The response will contain the data being retreived
} );
task.Wait();
int result = task.Result;
Console.WriteLine(result);
return result;
}
I want my async code to run in the lambda expression and get back the result from the server.
But i get back the error:
error CS4010: Cannot convert async lambda expression to delegate type 'Func<int>'. An async lambda expression may return void, Task or Task<T>, none of which are convertible to 'Func<int>'.
It says i can only return a void, task or task<> and to my understanding im returning
task<int>
Is this a problem with what im returning or is this because of async lambda?
Thanks
Edit:
response.ResultAs<int>()
Returns an Int but being inside a Task function it should be returned as a Task
Your whole method is suboptimal. You could rewrite your code to be much simpler. A few comments on your existing code first, however.
You're using Task.Factory.StartNew(), which is dangerous. In
most cases you should simply use Task.Run()
You're using Task.Wait() and Task.Result, which is also suboptimal, not to mention, that Task.Result includes Task.Wait() when accessing it. But I guess, that you want to test the lambda, so it's ok here.
The ResultAs<T>() method converts the response into an int (in your case). The method itself is defined as public virtual T ResultAs<T>(). It needn't return a Task<int>, because your lambda is asynchronous. If you'd remove the async from the lambda you'd have to return a Task<int>, but you can'T do that by simply changing the ResultAs<T> to ResultAs<Task<int>>, you'd have to use a TaskCompletionSource.
Based on the above that we can rewrite your method to this:
private int Server_Get_Int(){
var task = Task.Run(async () => {
var c = Server_Connect();
return (await c.GetAsync("todos/set")).ResultAs<int>();
});
int result = task.Result;
Console.WriteLine(result);
return result;
}
A more concise approach could look like this:
private async Task<int> Server_Get_Int_Async(){
return await Task.Run(async () => {
var c = Server_Connect();
return (await c.GetAsync("todos/set")).ResultAs<int>();
});
}
This creates a new task via Task.Run() and returns that to be completed later.
Based on the comments here are tow ways how you'd call the Server_Get_Int_Asnyc() method. I used explicit types so you can follow my comment, but in almost any case it's better to use var, because the compiler than can choose the best type for the job.
public async Task Foo()
{
// This is the fetch task that's going to be completed sometime in the future. You should almost in any case use configure await on your tasks. For reasons see below.
Task<int> intTask = Server_Get_Int_Async().ConfigureAwait(false);
// Do something other with the task object
// Finally await it and print
int result = await intTask;
Console.WriteLine(result);
}
// Do this if you just need the result and nothing else.
public async Task Bar()
{
int result = await Server_Get_Int_Async().ConfigureAwait(false);
Console.WriteLine(result);
}
In the end it seems, you're pretty new to Task based programming with async/await. I recommend you read the (excellent) introduction article written by Stephen Cleary and go on from there.
The beauty of async/await is, that it propagates naturally through your code and you can write asynchronous code almost like you'd write synchronous code.
Also, placing another article here on why you shouldn't use Wait() or Result to simply get the return value of the async method, since it's going to be noticed much better:
https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Test code (console app). This correctly shows "Result: 10".
static void Main(string[] args)
{
Func<Task<int>> func = async () => { await Task.Delay(1000); return 10; };
var task = Task.Factory.StartNew(func);
task.Wait();
int result = task.Unwrap().Result;
WriteLine($"Result: {result}");
ReadKey(true);
}

Return a Task<T> from a method that passes its result to an Action<T> parameter

I have a service I need to connect to that passes data back through an action like this:
public Guid UpdateEntities<T>(Action<EntitiesChangedResponse<T>> onResponse, IEnumerable<T> entities)
{
//Get some data
onResponse.Invoke(response);
{
Existing code would call the service as follows:
Guid requestId = _productService.UpdateEntities<Product>(x => OnEntitiesUpdated(x), new List<Product> { updateProduct1, updateProduct2 });
And the callback would do something with the result at some point in the future:
private void OnEntitiesUpdated<T>(EntitiesChangedResponse<T> response)
{
//Do something with the result
}
I'm try to integrate it with a task based signal R hub so need to return the operation as a typed task, but I can't for the life of me figure out how to achieve this (I'm quite new to tasks so please tell me if this is daft).
It would look something like this:
public Task<EntitiesChangedResponse<Product>> UpdateProducts(List<Product> products)
{
//Somehow wrap this in a task
Task<EntitiesChangedResponse<Product>> result = New Task<EntitiesChangedResponse<Product>>( call the product service );
return result;
}
Any help appreciated. It is hurting my head.
To make a bridge between "callback" API and task based API you can use TaskCompletionSource
public Task<EntitiesChangedResponse<Product>> UpdateProducts(List<Product> products)
{
var tcs = new TaskCompletionSource<EntitiesChangedResponse<Product>>();
_productService.UpdateEntities<Product>(response => tcs.SetResult(response), new List<Product> { updateProduct1, updateProduct2 });
return tcs.Task;
}
Let's have a look at the following method:
public Task<EntitiesChangedResponse<T>> UpdateEntities<T>(IEnumerable<T> entities)
{
var updateTask = Task.Run(()=>
{
//return data from this lambda expression
});
return updateTask;
}
This starts and returns a task which does your work.
(If you really need the GUID you can return a Tuple or any DTO containing both).
You now have a couple of options to consume this task, here's one:
private Task async UpdateEntitiesAsync<T>(IEnumerable<T> entities)
{
try
{
EntitiesChangedResponse<T> response = await UpdateEntities(entities);
//asynchronous callback implementation code can go here
}
catch(AggregateException ex)
{
//handle a thrown exception
}
}
Do note:
1)The execution continues past the 'await' statement once the task completes and will execute the following LOC.
2) if the task faults (throws an exception) the LOC past the await block will not execute and the catch clause will execute instead.
3) Calling the UpdateEntitiesAsync method does not block the calling thread. the execution immediately returns to the caller once the await statement is hit.
4) You can await the task returned from the async method as well since it (implicitly) returns a task which finishes once this method executes in full, making this method awaitable in itself, if such a need arises.
Alternatively, you can have this method return void, if you do not really care about when it completes.
5) If you do not catch the exception in this method, know that the task returned from the async method will fault, and must be handled somewhere up the invocation chain.
You can do this by either awaiting the task, accessing the Exception property, or invoking the Wait method of the task.
6) The Async postfix in the method's name is merely a naming convention, applied so that client code is aware of the method's asynchronous nature.

Cannot convert lambda expression to type "..." because it is not a delegate type

Good day!
I am trying to write an anonymous method using lambda expressions which would return an object from an async task. I would like to do this in the constructor, so that is the reason I can't make its parent method async.
The ReadJsonAsync method returns a Session object.
I will show you the relevant code:
Session session;
fileService = new FileService();
session = async () => { return await fileService.ReadJsonAsync() };
Thanks in advance!
If you want an Anonymous Method, you'll have to declare one which returns a Task<Session> as it is marked with the async modifier, hence must return a void (only for async event handlers), Task or Task<T> :
Func<Task<Session>> anonFunction = async () => await fileService.ReadJsonAsync();
If all you do is run ReadJsonAsync, you may also save yourself the state machine generation like so:
Func<Task<Session>> anonFunction = fileService.ReadJsonAsync;
Then you can await on it higher up the call stack:
Func<Task<Session>> anonFunction = fileService.ReadJsonAsync;
await anonFunction();
To add to Yuval's useful answer, if you just want to await an inline function, then the magic syntax is:
await ((Func<Task>)(async () =>
{
//async function code
}
))();
Note the extra brackets on the end to call the lambda immediately after declaration. Obviously if your function returns a type, then it would be Func<Task<Whatever>>
Useful if you're using Task.WhenAny() for example to await both an inline function and a timeout task.

Callbacks in C# taking the callback directly in the method like in javascript

I have the function in javascript which is something like
dothis(variablea, function(somevalue) {
..
});
which comes from function dothis(variablea, callback) {..}
So I want to fire dothis then callback the callback function later, when I get a response from a server.
How would I go about implementing something like this in C#, I've had a look at a couple of examples but I would like to pass the callback function directly into the method. Is this possible?
Absolutely - you basically want delegates. For example:
public void DoSomething(string input, Action<int> callback)
{
// Do something with input
int result = ...;
callback(result);
}
Then call it with something like this:
DoSomething("foo", result => Console.WriteLine(result));
(There are other ways of creating delegate instances, of course.)
Alternatively, if this is an asynchronous call, you might want to consider using async/await from C# 5. For example:
public async Task<int> DoSomethingAsync(string input)
{
// Do something with input asynchronously
using (HttpClient client = new HttpClient())
{
await ... /* something to do with input */
}
int result = ...;
return result;
}
The caller can then use that asynchronously too:
public async Task FooAsync()
{
int result1 = await DoSomethingAsync("something");
int result2 = await AndSomethingElse(result1);
Console.WriteLine(result2);
}
If you're basically trying to achieve asynchrony, async/await is a much more convenient approach than callbacks.
You're looking for delegates and lambda expressions:
void DoSomething(string whatever, Action<ResultType> callback) {
callback(...);
}
DoSomething(..., r => ...);
However, you should usually return a Task<T> instead.

Need to wrap my head around Async operations

I have custom control and I have interface this control exposes to it's users.
public interface ILookupDataProvider
{
string IdColumnName { get; }
IEnumerable<IDataColumn> Metadata { get; set; }
void GetDataAsync(string parameters,
Action<IEnumerable<object>> onSuccess, Action<Exception> onError);
}
So, it's my attempt to expose async operation in GetDataAsync
But I don't know how to implement this method in my class that implements interface. I understand this portion as I have method which will execute and then onCompletion, onSucess or onError delegate will be called.
Can somebody help with syntax on how to write those?
EDIT:
It's 4.0 and I can't use await commands
EDIT 2:
I use DevForce framework to load data, but for the sake of this sample - let's do WCF service for example. How would I wrap WCF service call in my interface implementation?
Also, do you think it's OK to create interface like this to present async operation? Would you do it differently with event for example?
The basic idea here is that the query will occur an a background thread. Once your operation is complete you would use the onSuccess and onError callbacks in order to report the new values. For example
void GetDataAsync(
string parameters,
Action<IEnumerable<object>> onSuccess,
Action<Exception> onError) {
WaitCallback doWork = delegate {
try {
IEnumerable<object> enumerable = GetTheData(parameters);
onSuccess(enumerable);
} catch (Exception ex) {
onError(ex);
}
};
ThreadPool.QueueUserWorkItem(doWork, null);
}
You really don't want to use this pattern:
void GetDataAsync(string parameters,
Action<IEnumerable<object>> onSuccess, Action<Exception> onError);
Instead, you want to use this:
Task GetDataAsync(string parameters);
In returning a Task, you are returning an instance which represents the asynchronous unit of work. From there, the consumer of the API can choose to call ContinueWith and decide what to do when it succeeds, or if there is an error.
However, there is a design flaw in your example. In a method named GetDataAsync, I'd expect data to be returned. That's not a problem, you can just change the signature to:
Task<MyData> GetDataAsync(string parameters);
This now returns a Task<T> which you can use the Result property of to get the result (it will block if the task isn't done), or you can use the ContinueWith method again to process the data when the async operation is done.
Additionally, you can take a CancellationToken structure instance of a parameter to determine if you should cancel your operation:
Task<MyData> GetDataAsync(string parameters,
CancellationToken cancellationToken);
Again, ContinueWith will allow you to indicate the action to take on cancellation.
Note that this is the way how methods using await and async in the Async CTP are currently being modeled; they are returning Task or Task<T>; doing the same in your code will allow you to be ready when these language features are baked in.
To use tasks you can use it like this. The only tricky thing to remember is to execute the callback in your UI thread which is achieved with TaskScheduler.FromCurrentSynchronizationContext() so you can update your UI or display a messagebox if something went wrong.
Due to the event driven nature of this stuff it can happen that if you hammer the button which does start the WCF calls that you get the results not back in the order you did send the requests. You can prevent this by storing the started task and cancel the last started task if you want to start a new operation or you can simply ignore the subsequent requests while a task is running.
private void button1_Click(object sender, EventArgs e)
{
GetDataAsync("www.data.com").ContinueWith(result =>
{
if (result.Exception != null)
{
MessageBox.Show(this, "Error: {0}" + result.Exception, "Error");
}
else
{
foreach (var obj in result.Result)
{
textBox1.Text += obj.ToString();
}
}
},
TaskScheduler.FromCurrentSynchronizationContext()
);
}
Task<IEnumerable<object>> GetDataAsync(string parameters)
{
return Task<IEnumerable<object>>.Factory.StartNew(() =>
{
Thread.Sleep(500);
// throw new ArgumentException("uups");
// make wcf call here
return new object[] { "First", "second" };
});
}

Categories