When is it asynchronous and awaiting in C #?

Exactly as the title says. Interestingly, I am writing an asynchronous call and I wait when it is not needed.

I have seen similar methods with async tag

public async Task CreateAsync(User user)
{
    if (_context.Entry<User>(user).State == EntityState.Detached)
    {
        _context.Set<User>().Add(user);
    }

    _context.Entry<User>(user).State = EntityState.Added;

    await _context.SaveChangesAsync();
}

      

and without it

public Task CreateAsync(User user)
{
    if (_context.Entry<User>(user).State == EntityState.Detached)
    {
        _context.Set<User>().Add(user);
    }

    _context.Entry<User>(user).State = EntityState.Added;

    return _context.SaveChangesAsync();
}

      

Both compile fine. I always add async and wait for keywords and wonder maybe if I am doing it wrong and write them when not needed?

EDIT:

If you are indeed returning a value, should it be written with or without the async / await keywords. Here is the keyword version

public async Task<User> CreateAsync(User user)
{
    if (_context.Entry<User>(user).State == EntityState.Detached)
    {
        _context.Set<User>().Add(user);
    }

    _context.Entry<User>(user).State = EntityState.Added;

    await _context.SaveChangesAsync();

    return user;
}

      

here is another example

public Task<User> FindByIdAsync(long userId)
{
    return _context.Users.FindAsync(userId);
}

public async Task<User> FindByIdAsync(long userId)
{
    return await _context.Users.FindAsync(userId);
}

      

EDIT 2

Great answers so far, but the last example. Since I have my async calls, how would I go about calling multiple async functions from 1 method. here is an example of what I have, but I don't know if this is correct. I don't want the method to complete until all AddAsync methods have completed. Is it correct,

private async Task AddPermissions(DataContext context)
{
    var permissionService = new PermissionService(context);

    await permissionService.AddAsync(new Permission("CanView", "View company"));
    await permissionService.AddAsync(new Permission("CanAdd", "Add and view company"));
    await permissionService.AddAsync(new Permission("CanEdit", "Edit and view company"));
    await permissionService.AddAsync(new Permission("CanDelete", "Delete and view company record"));

    await permissionService.AddAsync(new Permission("CanAdd", "Add new pages"));
    await permissionService.AddAsync(new Permission("CanEdite", "Edit existing pages"));
    await permissionService.AddAsync(new Permission("CanDelete", "Delete a page"));

    await permissionService.AddAsync(new Permission("CanAdd", "Add new page content"));
    await permissionService.AddAsync(new Permission("CanEdit", "Edit existing page content"));
    await permissionService.AddAsync(new Permission("CanDelete", "Delete page content"));
}

      

+3


source to share


4 answers


IMHO you will need to use await

if you want the operation to SaveChangesAsync

complete when the method returns or you need to do something with the result of the async operation. In this case, you are not doing anything about it, so it is best not to have an asynchronous method and avoid generating a state machine with the method , which leads to more efficient code.

As for your second edit, you are correct. Although the method will return as soon as the first one is encountered await

, all other expected statements will be executed one by one in the thread pool and then the result of the task will be updated. Therefore, if you are await AddPermissions

, this statement will only complete after all internal calls have completed permissionService.AddAsync

, unless an exception is thrown.

It is also possible to make parallel calls permissionService.AddAsync

, if needed, by storing the returned tasks in a list and then waitingTask.WhenAll



private async Task AddPermissions(DataContext context)
{
    var permissionService = new PermissionService(context);

    List<Task> permissionRequests = new List<Task>();

    permissionRequests.Add(permissionService.AddAsync(new Permission("CanView", "View company")));
    permissionRequests.Add(permissionService.AddAsync(new Permission("CanAdd", "Add and view company")));
    permissionRequests.Add(permissionService.AddAsync(new Permission("CanEdit", "Edit and view company")));
    permissionRequests.Add(permissionService.AddAsync(new Permission("CanDelete", "Delete and view company record")));

    permissionRequests.Add(permissionService.AddAsync(new Permission("CanAdd", "Add new pages")));
    permissionRequests.Add(permissionService.AddAsync(new Permission("CanEdite", "Edit existing pages")));
    permissionRequests.Add(permissionService.AddAsync(new Permission("CanDelete", "Delete a page")));

    permissionRequests.Add(permissionService.AddAsync(new Permission("CanAdd", "Add new page content")));
    permissionRequests.Add(permissionService.AddAsync(new Permission("CanEdit", "Edit existing page content")));
    permissionRequests.Add(permissionService.AddAsync(new Permission("CanDelete", "Delete page content")));

    await Task.WhenAll(permissionRequests);
}

      

So each call permissionService.AddAsync

triggers a request and adds the corresponding task to the list. Once you have released all the requests, you can await

do all of their completion with a help await Task.WhenAll

, this will wait until they complete or return an error. Any exceptions thrown will be stored in the task returned from Task.WhenAll

. Pending this task, the first exception is recreated, but you can access all of them using the property Task.Excpetion

that contains AggregatedException

, which in turn contains all the thrown exceptions.

+3


source


The topic may be deep, but here's a high-level overview.

There is a key difference between the two versions of the code you posted above. But the similarity is important first .

They both report _context

to save changes asynchronously. Since it is implemented SaveChangesAsync

.


Now the difference ..

In the first version, when you use the keyword async

, and await

any code that can be after a call await

(in this case, the call await

is the last call), the compiler makes it a sequel, and it should be carried out after the completion of the call await

(asynchronously). This includes any exception handling that can wrap an asynchronous call.



In contrast, in the second version, when we take the return value of an asynchronous call as Task

, then when the async operation was started and yet to complete, execution will continue in that method ( not being continued by the compiler). There is no code to execute after the async operation completes (unless you explicitly use .ContinueWith

for an object Task

).


Why are you using one over the other?

Again, at a high level async

and await

should be good for normal scenarios where you want to use the compiler to do magic for you to make it easier to work with asynchronous calls. However, it is also less flexible in some scenarios. Example. What if you want to run 10 ping operations asynchronously and then write a continuation when all 10 are finished. The same is not possible with using async

and await

keywords for each asynchronous ping. (The first call to wait makes the rest of the code a continuation of the first asynchronous call.)


Read more about the Internet at async

await

. It can get pretty deep, but it's worth understanding the details.

0


source


The second version is slightly more efficient, but it prevents you from going back and adding additional code later without rewriting it to use async / await.

That's pretty much it. I wouldn't complain if I saw any template in my codebase.

0


source


An easier way would be to call Task.Factory.StartNew

Task.Factory.StartNew(() => new Permission("CanView", "View company"));
Task.Factory.StartNew(() => new Permission("CanAdd", "Add and view company"));
...

      

0


source







All Articles