I would like to reuse the following static generic Profile function in my application:
private static async Task<T> Profile<T>(Func<Task<T>> func, string operation)
{
Console.WriteLine($"{operation} is called");
return await func();
}
And I have the following interface to implement:
public interface ICustomerOperations
{
Task<Customer> GetCustomerAsync(string id);
Task DeleteCustomerAsync(string id);
}
I can use Profile method with GetCustomerAsync without any problem.
public async Task<Customer> GetCustomer(string id)
{
return await Profile(() => _customerOperations.GetCustomerAsync(id), $"GetCustomerAsync");
}
However when I try to use Profile with DeleteCustomerAsync
public async void DeleteCustomer(string id)
{
await Profile(() => _customerOperations.DeleteCustomerAsync(id), $"DeleteCustomerAsync");
}
Build failed:
The type arguments for method 'CrmService.Profile(Func<Task>,
string)' cannot be inferred from the usage. Try specifying the type
arguments explicitly.
So my question is how can I reuse Task<T> with void?
You can create an overload without duplicating the method:
private static Task Profile(Func<Task> func, string operation)
{
return Profile<object>(async () => { await func(); return null; }, operation);
}
A simple solution is to duplicate your method:
private static async Task Profile(Func<Task> func, string operation)
{
Console.WriteLine($"{operation} is called");
await func();
}
I think duplicating your method is cleaner than doing some hacks like this:
public async void DeleteCustomer(string id)
{
await Profile(async () => { await _customerOperations.DeleteCustomerAsync(id); return 0; }, $"DeleteCustomerAsync");
}
Related
I'm trying to make this code working:
protected async Task RunIsolated<TServ1, TServ2>(Action<TServ1, TServ2> action)
{
await RunInScope(action, typeof(TServ1), typeof(TServ2));
}
protected async Task<TResult> RunIsolatedForResult<TService, TResult>(Func<TService, TResult> func)
{
return (TResult) await RunInScope(func, typeof(TService));
}
private Task<object> RunInScope(Delegate d, params object[] args)
{
using (var scope = _serviceProvider.CreateScope())
{
object[] parameters = args.Cast<Type>().Select(t => scope.ServiceProvider.GetService(t)).ToArray();
return Task.FromResult(d.DynamicInvoke(parameters));
}
}
this work for sync version of code, like this:
await RunIsolated<Service>(serv => serv.SaveAsync(item).Wait());
but don't work (db operation throw an exception) for async version of the same code
await RunIsolated<Service>(async serv => await serv.SaveAsync(item));
Is it somehow possible to convert async Action or Func to Delegate and invoke it without loosing async state?
You need to create new overload which accepts Func<Task>. Right now, anonymous async function you pass here
await RunIsolated<Service>(async serv => await serv.SaveAsync(item));
Is treated as Action, which means that is async void method basically, with all corresponding drawbacks. Instead you have to do something like this (simplified to use basic Action and Func, adjust as needed to your situation):
protected Task RunIsolated(Action action) {
return RunInScope(action);
}
protected Task RunIsolated(Func<Task> action) {
return RunInScope(action);
}
protected Task<TResult> RunIsolatedForResult<TResult>(Func<Task<TResult>> action) {
return RunInScopeWithResult<TResult>(action);
}
protected Task<TResult> RunIsolatedForResult<TResult>(Func<TResult> action) {
return RunInScopeWithResult<TResult>(action);
}
private async Task RunInScope(Delegate d, params object[] args) {
// do some stuff
using (var scope = _serviceProvider.CreateScope()) {
object[] parameters = args.Cast<Type>().Select(t => scope.ServiceProvider.GetService(t)).ToArray();
var result = d.DynamicInvoke(parameters);
var resultTask = result as Task;
if (resultTask != null) {
await resultTask;
}
}
}
private async Task<TResult> RunInScopeWithResult<TResult>(Delegate d, params object[] args) {
// do some stuff
using (var scope = _serviceProvider.CreateScope()) {
object[] parameters = args.Cast<Type>().Select(t => scope.ServiceProvider.GetService(t)).ToArray();
var result = d.DynamicInvoke(parameters);
var resultTask = result as Task<TResult>;
if (resultTask != null) {
return await resultTask;
}
return (TResult) result;
}
}
I have this function:
private async Task Wizardry<T>(Func<T theParameter, Task> method)
{
try
{
await method(theParameter);
}
catch
{ }
}
and the way I see it working is like this:
await this.Wizardry<Email>(this.emailProvider.SendAsync(email));
await this.Wizardry<Log>(this.SaveLog(log));
but obviously that does not work.
Does anyone know how I can achieve this?
Is this what you need:
private async Task Wizardry<T>(Func<T, Task> method, T theParameter)
{
try
{
await method(theParameter);
}
catch
{
}
}
And invoke it like:
await this.Wizardry<string>((z)=> Task.Run(()=>Console.WriteLine(z)), "test");
You are attempting to create a Func where you want to pass in parameters while you haven't got any parameters to pass in.
A non-generic Func<Task> will do:
await this.Wizardry(() => this.emailProvider.SendAsync(email));
await this.Wizardry(() => this.SaveLog(log));
private async Task Wizardry(Func<Task> method)
{
await method();
}
I can see 2 possibilities:
private async Task Wizardry(Func<Task> method) {
try {
await method();
} catch {
}
}
Which is called with:
this.Wizardry(() => this.emailProvider.SendAsync(email));
Or
private async Task Wizardry<T>(Func<T, Task> method, T theParameter) {
try {
await method(theParameter);
} catch {
}
}
Which is called with:
this.Wizardry(this.emailProvider.SendAsync, email);
What I would like to write is the following:
async void Foo()
{
var result = await GetMyTask().IgnoreCancelAndFailure();
ProcessResult(result);
}
Instead of:
void Foo()
{
GetMyTask().ContinueWith(task => ProcessResult(task.Result),
TaskContinuationOptions.OnlyOnRanToCompletion);
}
However I don't know how to implement the method IgnoreCancelAndFailure, which would have the following signature:
//On cancel or failure this task should simply stop and never complete.
Task<T> IgnoreCancelAndFailure<T>(this Task<T> task)
{
throw new NotImplementedException();
}
If possible, how should I implement IgnoreCancelAndFailure?
You could do something like that, but you need to know what you want the method to return in case of failure, since a return value is expected:
public static async Task<T> IgnoreCancelAndFailure<T>(this Task<T> task)
{
try
{
return await task;
}
catch
{
return ???; // whatever you want to return in this case
}
}
If it's a Task with no result, just leave the catch empty (or perhaps log the exception... swallowed exceptions make for hard debugging)
If you just want to execute ProcessResult only when GetMyTask succeeds, you can do this:
async void Foo()
{
try
{
var result = await GetMyTask();
ProcessResult(result);
}
catch(Exception ex)
{
// handle the exception somehow, or ignore it (not recommended)
}
}
You will never be able to stop your code from continuing expect when killing the thread or process. keep in mind that the await task can be considered a function call that will always have to return a value or throw an exception.
The closest way to shorten your code is creating a wrapper function that uses the ProcessResult method as Action argument.
Something like that:
public static async Task IgnoreCancelAndFailure<T>(this Task<T> task, Action<T> resultProcessor)
{
task.ContinueWith(t => resultProcessor(t.Result),
TaskContinuationOptions.OnlyOnRanToCompletion);
}
async void Foo()
{
GetMyTask().IgnoreCancelAndFailure(ProcessResult);
}
I think I found the answer. The following seems to do the trick. It uses the awaitable pattern. Could you guys confirm that this isn't evil?
class User
{
async void Foo()
{
var result = await GetMyTask().IgnoreCancelAndFailure();
ProcessResult(result);
}
}
public static class TaskExtenstions
{
public static SilentTask<T> IgnoreCancelAndFailure<T>(this Task<T> task)
{
return new SilentTask<T>(task);
}
}
public class SilentTask<T>
{
private readonly Task<T> _inner;
public SilentTask(Task<T> inner)
{
_inner = inner;
}
public SilentAwaiter GetAwaiter()
{
return new SilentAwaiter(_inner);
}
public class SilentAwaiter : INotifyCompletion
{
private readonly TaskAwaiter<T> _inner;
private readonly Task<T> _task;
public SilentAwaiter(Task<T> task)
{
_task = task;
_inner = task.GetAwaiter();
}
public bool IsCompleted
{
get
{
return _task.Status == TaskStatus.RanToCompletion;
}
}
public void OnCompleted(Action continuation)
{
_inner.OnCompleted(() =>
{
if (IsCompleted)
{
continuation();
}
});
}
public T GetResult()
{
return _inner.GetResult();
}
}
}
I want to pass in a void or an int/string/bool(Which returns a value) Dynamically like so.
Delay(MyVoid);//I wont to execute a delay here, after the delay it will execute the the param/void like so...
public static void MyVoid()
{
MessageBox.Show("The void has started!");
}
public async Task MyAsyncMethod(void V)
{
await Task.Delay(2000);
V()
}
ps, I have tried using Delegates but it doesn't let be use it as a parameter.
Use an Action delegate to execute a method which returns void:
public async Task MyAsyncMethod(Action V)
{
await Task.Delay(2000);
V();
}
Or Func<T> for a method which returns some value
public async Task MyAsyncMethod(Func<int> V)
{
await Task.Delay(2000);
int result = V();
}
I'm trying to get the hand of the new async CTP stuff and I'm probably confusing myself here..
I can have this "task method", with no problem:
public static Task<String> LongTaskAAsync() {
return Task.Run(() => {
return("AAA");
});
}
But what if I need the task to execute another task, can I mark it as "async" and use "await"? I tried this:
public async static Task<String> LongTaskAAsync() {
await Task.Delay(2000);
return Task.Run(() => {
return("AAA");
});
}
But then mysteriously get this compiler error: Since this is an async method, the return expression must be of type 'string' rather than Task<string>
What am I missing here?
You may want to read my async/await intro post.
Return values from async methods are wrapped in a Task<TResult>. Likewise, await unwraps those return values:
public static async Task<String> LongTaskAAsync() {
await Task.Delay(2000);
return await Task.Run(() => {
return("AAA");
});
}
The reasoning behind this is described in my Async "Why Do the Keywords Work That Way" Unofficial FAQ.
P.S. You can also use Task.FromResult for simple tests like this.
Edit: If you want to create and return the Task object itself, then the method should not be async. One somewhat common pattern is to have a public non-async method that calls the async portion only if necessary.
For example, some kind of asynchronous cache - if the object is in the cache, then return it immediately; otherwise, asynchronously create it, add it to the cache, and return it (this is example code - not thread-safe):
public static Task<MyClass> GetAsync(int key)
{
if (cache.Contains(key))
return Task.FromResult(cache[key]);
return CreateAndAddAsync(key);
}
private static async Task<MyClass> CreateAndAddAsync(int key)
{
var result = await CreateAsync(key);
cache.Add(key, result);
return result;
}
Can a “task method” also be an “async” method?
Yes it can be, by simply changing the method signature to public async static Task<Task<String>> LongTaskAAsync() since that is, what it will return.
If you use the async keyword, the runtime will wrap the type you return into a task, to enable asynchronousness. Say if you return a string, the runtime will wrap that into a Task<string>. int will go Task<int> and Task<string> will go Task<Task<string>>. See this console app to clearify:
public class Program
{
public static void Main(string[] args)
{
// start the main procedure asynchron
Task.Run(() => DoIt()).Wait();
}
// for async support since the static main method can't be async
public static async void DoIt()
{
Program p = new Program();
// use the methods
string s = await p.GetString();
int i = await p.GetInt();
Task<string> tsk = await p.GetTaskOfString();
// just to prove the task works:
// C# 5
string resultFromReturnedTask = await tsk;
// C# 4
string resultFromReturnedTask2 = tsk.Result;
}
public async Task<string> GetString()
{
return "string";
}
public async Task<int> GetInt()
{
return 6;
}
public async Task<Task<string>> GetTaskOfString()
{
return Task.Run(() => "string");
}
}