1

I'm working on a series of methods that execute many different database calls using entity framework. Many of these methods can run asynchronously as I really don't care about their output, and don't rely on them.

However, when I try implementing certain methods, I get a warning from the compiler saying: "Because this call is not awaited, the current method continues to run before the call is completed"

But to me, this seems like that's my desired behavior, as I don't care what the methods do.

Here's an example of the methods

public async Task SetupAccessControl(int objectTypeId, int objectId, int? organizationId)
{
    using (var context = new SupportContext(CustomerId))
    {
        ... // omitted for brevity
        if (objectTypeId == (int) ObjectType.User)
        {
            AddToUserRoleBridge("Everyone", objectId);//Warning on this line
            AddToUserRoleBridge("Default", objectId); //Warning on this line
        }
        ... // omitted for brevity
    }
}

public async Task AddToUserRoleBridge(string role, int userId)
{
    using (var context = new SupportContext(CustomerId))
    {
        var defaultRole = context.Roles.FirstOrDefault(n => n.Name == role);
        if (defaultRole != null)
        {
            var urb = new UserRoleBridge
            {
                RoleId = defaultRole.Id,
                UserId = userId
            };

            context.UserRoleBridges.Add(urb);
            await context.SaveChangesAsync();
        }
    }
} 

edit

Essentially, when I run the main function, I want a series of method calls to all fire off somewhat simultaneously and handle everything in their own threads so that I don't have to worry about it. Here is a pseudo-code example.

public async void RunAllAsync() {
    taskA(*some value*);
    taskA(*some value*);
    taskB(*some value*);
    taskB(*some value*);

    await AllTasksCompleted
}

public async Task taskA(int item){
     //do something with item
}


public async Task taskB(int item) {
    subTaskB(*some value*)
    subTaskB(*some value*)
}

public async Task subTaskB(int item) {
    // do something
} 

In the above example, when #RunAllAsync is called, every function call it makes (and the function calls they make) are fired off simultaneously. When all of these calls are completed, whatever method called #RunAllAsync would continue to execute.

7
  • Have a look at this: msdn.microsoft.com/en-us/library/… Has a nice diagram showing how an async method runs. Commented Oct 2, 2015 at 16:21
  • Does your program still execute correctly when it terminates before that Task is done? How about it failing on an exception, one you'll never see? The correct answer to those questions is almost always a resounding no. Read this. Commented Oct 2, 2015 at 16:44
  • as I don't care what the methods do Really? Are you sure? You don't care when they complete? You don't care whether or not they complete? You don't care if they throw an exception? While a true "fire-and-forget" scenario can happen, it's far less common than most people think. Commented Oct 2, 2015 at 18:06
  • I neglected to put in the entirety of each function. However, there are error handlers within each function that check for the completion. They also log everything in an audit trail so that everything can be monitored. By saying I didn't care what the methods did, I simply meant I didn't want to await any of the calls I made because I simply want them all to run fairly simultaneously. Commented Oct 2, 2015 at 18:11
  • I updated my main post with some simply pseudo code that illustrates somewhat what I'm trying to do. Commented Oct 2, 2015 at 18:18

2 Answers 2

2

If you're not using await, the async keyword doesn't really do anything useful and you can leave it off. You can await a method returning a Task regardless of whether it's marked as async or not.

public Task DoSomethingAsync()
{
    Task someTaskJustLikeANormalReturnValue = Task.Delay(1000);
    return someTaskJustLikeANormalReturnValue;
}

// later...
public async Task SomeOtherFunction()
{
    // You can await DoSomethingAsync just like any async method, because
    // you're really awaiting the Task which got returned.
    await DoSomethingAsync();
}

In your case I would probably collect the tasks and await them all together:

    public async Task SetupAccessControl(int objectTypeId, int objectId, int? organizationId)
    {
        var tasks = new List<Task>();
        using (var context = new SupportContext(CustomerId))
        {
            ... // omitted for brevity
            if (objectTypeId == (int) ObjectType.User)
            {
                tasks.Add(AddToUserRoleBridge("Everyone", objectId));
                tasks.Add(AddToUserRoleBridge("Default", objectId));
            }
            ... // omitted for brevity
        }

        await Task.WhenAll(tasks.ToArray());
    }

This allows you to pass the decision up to the caller of whether to await on the subtasks or not. This will also allow the any caller to unwrap any exceptions if they happen in AddToUserRoleBridge.

Sign up to request clarification or add additional context in comments.

6 Comments

"The only function of the async keyword is to enable the await keyword." Well, not really. It also changes how exceptions are propagated, and how return values are wrapped.
True, I rewrote the first sentence to make it more accurate and relevant to the question.
The first sentence is still not true though. Consider: public Task Foo() {} won't compile. public async Task Foo() {} will.
So in my code examples in the original post, what would be the proper way to structure them so that they run asynchronously?
Note that if you changed the second method in the question from using await context.SaveChangesAsync(); to return context.SaveChangesAsync() it would break (or worse, sometimes break) because it's not really a tail call, as it calls Dispose() (via using) after the await.
|
1

You can do it two ways:

  • Change the signature of AddToUserRoleBridge to return void will remove the warning (but nothing can await it then).
  • Store the result of the call. var ignoreme=AddToUserRoleBridge(...) will remove the warning as well.

1 Comment

Although this does answer your question, I highly recommend Erik's answer over doing this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.