16

I have this method in my service:

public virtual async Task<User> FindByIdAsync(string userId)
{
    this.ThrowIfDisposed();
    if (userId == null)
    {
        throw new ArgumentNullException("userId");
    }
    return await this.repository.FindByIdAsync(userId);
}

and then in a view I have this code:

using (var service = new UserService(new CompanyService(), User.Identity.GetUserId()))
{
    var user = service.FindByIdAsync(id);
}

but the user is the Task and not the User. I tried adding await to the service call, but I can't use await unless the current method is async. How can I access the User class?

r3plica
  • 13,017
  • 23
  • 128
  • 290

2 Answers2

14

The best solution is to make the calling method async and then use await, as Bas Brekelmans pointed out.

When you make a method async, you should change the return type (if it is void, change it to Task; otherwise, change it from T to Task<T>) and add an Async suffix to the method name. If the return type cannot be Task because it's an event handler, then you can use async void instead.

If the calling method is a constructor, you can use one of these techniques from my blog. It the calling method is a property getter, you can use one of these techniques from my blog.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • 2
    Say mate... Are you the core developer of the .net framework? You are deeply involved in concurrent development in c# – hackp0int Jan 02 '14 at 14:22
  • 1
    @IamStalker: Haha, no. I don't work for Microsoft. I just have a lot of experience in concurrent apps so I understand where they're coming from especially with `async`. There is a fellow named Stephen *Toub* who does work for Microsoft and has been deeply involved with TPL, Dataflow, and `async`. – Stephen Cleary Jan 02 '14 at 14:50
  • Stephen, The problem I have is that my View is virtual so it calls my service but it is not a function so I can't prepend async :( is there some other work around? – r3plica Jan 02 '14 at 18:28
  • The code calling `FindByIdAsync` *must* be in a method... Why can't you make that method `async`? – Stephen Cleary Jan 02 '14 at 19:15
13
  • Using this in async methods without special thread-locked object is dangerous
  • If you cannot use await, use a code like following.

    Task<User> task = TaskFindByIdAsync();
    
    task.Wait(); //Blocks thread and waits until task is completed
    
    User resultUser = task.Result;
    
Ilya Tereschuk
  • 1,204
  • 1
  • 9
  • 21
  • This is a really bad practice. It may be a hidden deadlock. [see this](http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html) – Olivier Jan 02 '14 at 14:08
  • @Olivier Looks like it does not depend so. Only unclear code in task [may cause a deadlock](http://stackoverflow.com/questions/13140523/await-vs-task-wait-deadlock) – Ilya Tereschuk Jan 02 '14 at 14:11
  • A good habit is to never rely on **how** methods are implemented when calling them. If the Wait() is on the context thread, and method Waited (not A-waited) uses an 'await', then you will deadlock (because it waits for the main thread to be available). – Olivier Jan 02 '14 at 14:19
  • It's unclear what you mean by 'Using this in async methods without special thread-locked object is dangerous'. Could you please clarify? – jeroenh Jan 02 '14 at 14:23
  • @jeroenh Because `async` methods are often ran **simultaneously** and they may encounter a concurrency conflict sharing `this`. For example, [singleton locks not on `this`, but on special `object` field](http://ilyatereschuk.blogspot.com/2013/12/c-simple-singleton-blank-for-everyday.html) – Ilya Tereschuk Jan 02 '14 at 16:08
  • 1
    Using '`this` in a 'readonly' way (i.e., reading fields) is perfectly safe, even multithreaded way. I see no need for locks in the code of the OP. – jeroenh Jan 02 '14 at 17:21
  • this is completely outdated and can't be used now. – DanielV Jun 13 '18 at 15:49
  • Thanks this helped me alot. – silentsudo Jan 30 '19 at 14:23