I've just, for a rough draft, converted a whole lot of data access methods to async using the following pattern, and it looks too simple to be good enough for later iterations. How safe is it, what is missing, and how should I be doing it?
The service that provides long-running calls:
private class UserService
{
public IdentityUser GetById(int id)
{
...
}
}
private UserService _userService = new UserService();
The original synchronous method:
public IdentityUser GetById(int id)
{
return _userService.GetById(id);
}
My fantastic new async method:
public async Task<IdentityUser> GetByIdAsync(int id)
{
await Task.Run(() => _userService.GetById(id));
}
You should not make "fake async" methods like this:
public async Task<IdentityUser> GetByIdAsync(int id)
{
await Task.Run(() => _userService.GetById(id));
}
The reason I call it "fake async" is because there is nothing intrinsically async about the operation. In this case, you should have only the synchronous method. If a caller wants to make it async by using Task.Run, he can do that.
When is something intrinsically async? When you make a request to a web service or a database, for example, between sending the request and receiving the response there is a waiting period - the request is an intrinsically async operation. To avoid blocking the calling thread, use async-await.
Technically, that works, but it works by creating a new thread to perform a synchronous operation, which itself is wrapping and blocking on an inherently asynchronous operation. That means you're not getting some of the biggest benefits of going async in the first place.
The right way is to go asynchronous all the way. Whereas right now you probably have something like this:
private class UserService
{
public IdentityUser GetById(int id)
{
return mContext.Users.Single(u => u.Id == id);
}
}
... you should now create an async version:
private class UserService
{
public async Task<IdentityUser> GetByIdAsync(int id)
{
return await mContext.Users.SingleAsync(u => u.Id == id);
}
}
Usage:
public async Task<IdentityUser> GetByIdAsync(int id)
{
return await _userService.GetByIdAsync(id);
}
Supposing, of course, that your underlying framework supports asynchronous methods like SingleAsync() for inherently asynchronous operations, this will allow the system to release the current thread while you wait for the database operation to complete. The thread can be reused elsewhere, and when the operation is done, you can use whatever thread happens to be available at that time.
It's probably also worth reading about and adopting these Best Practices. You'll probably want to use .ConfigureAwait(false) anywhere that you're not accessing contextual information like sessions and requests, for example.
This answer assumes, of course, that GetById is inherently asynchronous: you're retrieving it from a hard drive or network location or something. If it's calculating the user's ID using a long-running CPU operation, then Task.Run() is a good way to go, and you'll probably want to additionally specify that it's a long-running task in the arguments to Task.Run().
Task.Run () should only be used for CPU-bound work. Don't quite remember why though.
Try to make a GetByIdAsync () method instead, that ultimately calls an async resource.
Related
Is it a good idea to put the Enqueue method in an async wrapper and have the controller call it that way?
Which is better and why?
Synchronously:
public IActionResult AddLogs(List<Log> logs)
{
BackgroundJob.Enqueue(() => DM.AddLogsBackground(logs));
return Ok();
}
Async:
public async Task<IActionResult> AddLogs(List<Log> logs)
{
await DM.AddLogs(logs);
return Ok();
}
public async Task AddLogs(List<Log> logs)
{
BackgroundJob.Enqueue(() => AddLogsBackground(logs));
}
The documentation says:
The Enqueue method does not call the target method immediately, it
runs the following steps instead:
Serialize a method information and all its arguments.
Create a new background job based on the serialized information.
Save background job to a persistent storage.
Enqueue background job to its queue.
Which is better and why?
As BackgroundJob.Enqueue is not awaited (nor awaitable) it will just be processed synchronously.
So all the async syntax is useless and misleading here.
Note that your DM.AddLogsBackground can be an async method. But as far as I know, the processing will still be synchronous on the background job server.
I hope this is still on-topic. In this post here I saw how to create an await ViewAsync():
Returning a view with async keyword
So my consideration was: okay, I want to make my application use multithreading, let's make a BaseController that contains those methods for ViewAsync:
just a part of it:
public class BaseController : Controller
{
[NonAction]
public virtual async Task<ViewResult> ViewAsync()
{
return await Task.Run(() => this.View(null));
}
[NonAction]
public virtual async Task<ViewResult> ViewAsync(string viewName)
{
return await Task.Run(() => this.View(viewName, this.ViewData.Model));
}
// the other implementations....
}
now I could always call like this in the inheriting class:
[HttpGet]
public async Task<IActionResult> DoSomething()
{
// maybe we need to do something here, maybe not
return await ViewAsync(new DoSomethingObject());
}
imho, my advantage/target is performance since I always can use multithreading now.
Am I right with my consideration?
In the post, a comment to an answer started with I wouldn't do this. But there aren't many votes/comments/answers.. Where are risks or disadvantages of such an implementation? And maybe, why doesn't Microsoft.AspNetCore.Mvc.Controller come with the method ViewAsync?
Any web app already uses multithreading. When a request comes in, a thread from the thread pool handles it. Your app already handles multiple requests at the same time, without you using Task.Run.
Async/await stuff is useful so that you don't block threads from the thread pool while you wait for async operations to complete (like querying a DB).
Starting new tasks Task.Run on the default thread pool is pretty much useless, unless you are doing some CPU intensive work. Let's say you have an endpoint that calculates the prime numbers up to n on each request, in that case you could delegate a new task on the default thread pool that returns the prime numbers, and have a view that renders them to some html.
Rendering a view has nothing asynchronous about it, so a ViewAsync is not needed. Preparing the data for a view probably is async, but the rendering is not. Also, having a ViewAsync would probably over complicate the template syntax.
I have .NET core Web API which as service layer. Service layer has all EF code.
If have basecontroller with this code
protected Task<IActionResult> NewTask(Func<IActionResult> callback)
{
return Task.Factory.StartNew(() =>
{
try
{
return callback();
}
catch (Exception ex)
{
Logger.LogError(ex.ToString());
throw;
}
});
}
In controller action I wrap all calls to service in above method e.g. :
[HttpGet("something")]
public async Task<IActionResult> GetSomething(int somethingId)
{
return await NewTask(() =>
{
var result = _somethingService.GetSomething(somethingId);
if (result != null)
return Ok(result);
else
return NotFound("Role not found");
});
}
Is this correct pattern considering tomorrow I may have more than one service calls in action or making calls to other webservice. Please advise.
i want my api to benefit from async await thing.does above pattern will serve these needs
No, it does not. Running synchronous work on the thread pool gives you the drawbacks of synchronous and asynchronous code, with the benefits of neither.
something service has some crud operations which use entityframework core
Currently, your action method is what I call "fake asynchronous" - it looks asynchronous (e.g., using await), but in fact is just running blocking code on a background thread. On ASP.NET, you want true asynchrony, whicn means you must be async all the way. For more about why this is bad on ASP.NET, see the first half of my intro to async on ASP.NET article (it mostly deals with ASP.NET non-core, but the first part talking about synchronous vs asynchronous requests is valid for any kind of server).
To make this truly asynchronous, you should start at the lowest level - in this case, your EFCore calls. They all support asynchrony. So, replace API calls like x.FirstOrDefault() with await x.FirstOrDefaultAsync() (and the same for all your creates/updates/deletes, etc).
Then allow async/await to grow naturally from there; the compiler will guide you. You'll end up with asynchronous methods on your somethingService which can be consumed as such:
[HttpGet("something")]
public async Task<IActionResult> GetSomething(int somethingId)
{
var result = await _somethingService.GetSomethingAsync(somethingId);
if (result != null)
return Ok(result);
else
return NotFound("Role not found");
}
Okay, first of all, you should stop using Task.Factory.StartNew and use Task.Run only when you have heavy CPU-bound work that you want to run on a thread pool thread. In you case you don't really need that at all. Also you should remember that you should only use Task.Run when calling a method and not in the implementation of the method. You can read more about that here.
What you really want in your case is to have asynchronous work inside your service (I'm not really sure you even need a service in your case) when you are actually making a call to the database and you want to use async/await and not just run some stuff on a background thread.
Basically your service should look something like this (if you are sure you need a service):
class PeopleService
{
public async Task<Person> GetPersonByIdAsync(int id)
{
Person randomPerson = await DataContext.People.FirstOrDefaultAsync(x => x.Id == id);
return randomPerson;
}
}
As you can see your service now makes async calls to the database and that's basically what your pattern should be. You can apply this to all your operations(add/delete/ etc..)
After making your service asynchronous you should be easily able to consume the data in the action.
Your actions should look something like this:
[HttpGet("something")]
public async Task<IActionResult> GetPerson(int id)
{
var result = await PeopleService.GetPersonByIdAsync(id);
if (result != null)
return Ok(result);
else
return NotFound("Role not found");
}
I'm designing a repository, but I have this doubt.
Should I design it to be blocking operations or async?
I tend to think that blocking is more elegant, since users can wrap calls to be async when needed with something like
Task.Run( () => repository.Get(...));
What do you think?
Since underlying data source is naturally asynchronous in most cases (web service, database, file), async API is a preferred way.
blocking is more elegant, since users can wrap calls to be async when needed
Actually, vice versa.
User (not you!) can wrap async call into synchronous one, if needed:
Task<MyObj> DoSomethingAsync() { ... }
MyObj DoSomething()
{
return DoSomethingAsync().Result;
}
while this:
Task.Run( () => repository.Get(...));
is called "async over sync" and must be avoided:
should we expose an asynchronous entry point for a method that’s
actually synchronous? The stance we’ve taken in .NET 4.5 with the
Task-based Async Pattern is a staunch “no.”
I am working with async actions and use the HttpContext.Current.User like this
public class UserService : IUserService
{
public ILocPrincipal Current
{
get { return HttpContext.Current.User as ILocPrincipal; }
}
}
public class ChannelService : IDisposable
{
// In the service layer
public ChannelService()
: this(new Entities.LocDbContext(), new UserService())
{
}
public ChannelService(Entities.LocDbContext locDbContext, IUserService userService)
{
this.LocDbContext = locDbContext;
this.UserService = userService;
}
public async Task<ViewModels.DisplayChannel> FindOrDefaultAsync(long id)
{
var currentMemberId = this.UserService.Current.Id;
// do some async EF request …
}
}
// In the controller
[Authorize]
[RoutePrefix("channel")]
public class ChannelController : BaseController
{
public ChannelController()
: this(new ChannelService()
{
}
public ChannelController(ChannelService channelService)
{
this.ChannelService = channelService;
}
// …
[HttpGet, Route("~/api/channels/{id}/messages")]
public async Task<ActionResult> GetMessages(long id)
{
var channel = await this.ChannelService
.FindOrDefaultAsync(id);
return PartialView("_Messages", channel);
}
// …
}
I have the code recently refactored, previously I had to give the user on each call to the service.
Now I read this article https://www.trycatchfail.com/2014/04/25/using-httpcontext-safely-after-async-in-asp-net-mvc-applications/ and I’m not sure if my code still works.
Has anyone a better approach to handle this? I don’t want to give the user on every request to the service.
As long as your web.config settings are correct, async/await works perfectly well with HttpContext.Current. I recommend setting httpRuntime targetFramework to 4.5 to remove all "quirks mode" behavior.
Once that is done, plain async/await will work perfectly well. You'll only run into problems if you're doing work on another thread or if your await code is incorrect.
First, the "other thread" problem; this is the second problem in the blog post you linked to. Code like this will of course not work correctly:
async Task FakeAsyncMethod()
{
await Task.Run(() =>
{
var user = _userService.Current;
...
});
}
This problem actually has nothing to do with asynchronous code; it has to do with retrieving a context variable from a (non-request) thread pool thread. The exact same problem would occur if you try to do it synchronously.
The core problem is that the asynchronous version is using fake asynchrony. This inappropriate, especially on ASP.NET. The solution is to simply remove the fake-asynchronous code and make it synchronous (or truly asynchronous, if it actually has real asynchronous work to do):
void Method()
{
var user = _userService.Current;
...
}
The technique recommended in the linked blog (wrapping the HttpContext and providing it to the worker thread) is extremely dangerous. HttpContext is designed to be accessed only from one thread at a time and AFAIK is not threadsafe at all. So sharing it among different threads is asking for a world of hurt.
If the await code is incorrect, then it causes a similar problem. ConfigureAwait(false) is a technique commonly used in library code to notify the runtime that it doesn't need to return to a specific context. Consider this code:
async Task MyMethodAsync()
{
await Task.Delay(1000).ConfigureAwait(false);
var context = HttpContext.Current;
// Note: "context" is not correct here.
// It could be null; it could be the correct context;
// it could be a context for a different request.
}
In this case, the problem is obvious. ConfigureAwait(false) is telling ASP.NET that the rest of the current method does not need the context, and then it immediately accesses that context. When you start using context values in your interface implementations, though, the problem is not as obvious:
async Task MyMethodAsync()
{
await Task.Delay(1000).ConfigureAwait(false);
var user = _userService.Current;
}
This code is just as wrong but not as obviously wrong, since the context is hidden behind an interface.
So, the general guideline is: use ConfigureAwait(false) if you know that the method does not depend on its context (directly or indirectly); otherwise, do not use ConfigureAwait. If it's acceptable in your design to have interface implementations use the context in their implementation, then any method that calls an interface method should not use ConfigureAwait(false):
async Task MyMethodAsync()
{
await Task.Delay(1000);
var user = _userService.Current; // works fine
}
As long as you follow that guideline, async/await will work perfectly with HttpContext.Current.
Async is fine. The problem is when you post the work to a different thread. If your application is setup as 4.5+, the asynchronous callback will be posted in the original context, so you'll also have the proper HttpContext etc.
You don't want to access shared state in a different thread anyway, and with Tasks, you rarely need to handle that explicitly - just make sure you put all your inputs as arguments, and only return a response, rather than reading or writing to a shared state (e.g. HttpContext, static fields etc.)
There is no problem, if your ViewModels.DisplayChannel is a simple object without additional logic.
A problem may occur, if the result of your Task references to "some context objects", f.e. to HttpContext.Current. Such objects are often attached to the thread, but entire code after await may be executed in another thread.
Keep in mind, that UseTaskFriendlySynchronizationContext doesn't solve all your problems. If we are talking about ASP.NET MVC, this setting ensures that Controller.HttpContext contains correct value as before await as after. But it doesn't ensure that HttpContext.Current contains correct value, and after await it still can be null.