Reputation: 9818
Exactly as the title says. I wonder if i am writing async and await when it is not needed.
I have seen methods like this with the 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 like this 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 am always adding the async and await keywords and wonder maybe if i am doing it wrong and writing them when they are not needed?
EDIT:
If you are actually returning a value, should this be written using the async/await keywords, or without. Here is a version with the keywords
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
Excellent answers so far, but 1 last example. Since i have my async calls, how would i cope with calling multiple async functions from 1 method. here is an example of what i have, but i dont know if it is right. I do not want the method to exit until all AddAsync methods are completed. Is this 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"));
}
Upvotes: 3
Views: 905
Reputation: 10986
A simpler 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"));
...
Upvotes: 0
Reputation: 13495
IMHO you would only need to use the await
if you want the SaveChangesAsync
operation to have finished 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 with it so it is better to not have an async method and avoid the generation of the state machine withing the method, resulting in more efficient code.
Regarding your second edit, you are correct. Although the method will return as soon as the first await
is encountered all other awaited statements will be executed one after the other on the thread pool and then the result of the task will be updated. So if you await AddPermissions
that statement will only complete once all internal permissionService.AddAsync
calls are complete, unless an exception is thrown.
It is also possible to execute permissionService.AddAsync
calls in parallel if necessary, by storing the returned tasks in a list and then awaiting Task.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 to permissionService.AddAsync
kicks off the request and adds the corresponding task to the list. Once you have kicked off all the requests, you can await
all their completion with await Task.WhenAll
, this will wait until they are all complete or have returned an error. Any exceptions thrown will be stored in the task returned from Task.WhenAll
. Awaiting that task will rethrow the first exception, but you can access all of them using the Task.Excpetion
property which contains an
AggregatedException
which in turn contains all the thrown exceptions.
Upvotes: 3
Reputation: 4465
The topic can be deep, but here is a high level overview.
There is a key difference between the two versions of code you have posted above.. But first the important similarity..
They both tell _context
to save changes asynchronously.. Because that's how SaveChangesAsync
is implemented.
Now, the difference..
In first version, when you use async
and await
keyword, any code which may be after the await
call (in this case, the await
call is the last call in the function), compiler turns it into a continuation, and it is supposed to execute after the await
call finishes (asynchronously).
This includes any exception handling which might wrap the async call.
In contrast, in the second version, when we take the return value of the async call as a Task
, then while the async operation has been kicked off, and yet to finish, the execution will continue on in that method (It is not turned into a continuation by the compiler). No code is set to execute after the async operation finishes (unless you explicitly use .ContinueWith
on the Task
object).
Why would you use one over the other?
Again, at high level async
and await
should would fine for normal scenarios, where you want to advantage of compiler doing some magic for you, in order to make it easier to deal with async calls.. However, it is also less flexible for some scenarios. Example - What if you want to kick off 10 ping operations asynchronously, and then write a continuation when all 10 finish. The same is not possible using async
and await
keywords for each async ping. (First call to await makes rest of the code a continuation to first async call).
Search more on internet for more details on async
await
.. It can be pretty deep, but worth understanding the details.
Upvotes: 0
Reputation: 70287
The second version is slightly more efficient, but it doesn't allow you to go back and add more code later without rewriting it to use async/await.
That's pretty much it. I wouldn't complain if I saw either pattern in my code base.
Upvotes: 0