System.NotSupportedException on DBContext - c#

I am using MVC5 + Entity Framework and I am getting an strange exception.The exception seems to be due to a concurrency problem when multiple users are using the site at the same time. The exception says:
System.NotSupportedExceptionA second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.
Now based on the logs, the culprit is the following method:
public async Task<Customer> GetUserByUserIdAsync(string userId)
{
return await DbContext.Users.FirstOrDefaultAsync(u => u.Id == userId);
}
I can't see what is causing it. Looking at the error, which says use await...as you can see I have await behind it.
Anyone can help please?
Thank you.
Edit: My scenario is using Unit of Work and I have got several layers between controller and the depth of my data layer.

Related

Parallelizing synchronous tasks while retaining the HttpContext.Current in ASP.NET

I've scoured the SO for answers but found none that pertain to the problem at hand, although this one nails it on "why", but isn't solving it.
I have a REST endpoint that needs to gather data from other endpoints - in doing so, it accesses the HttpContext (setting authentication, headers, etc... all done with 3rd party lib I don't have access to).
Unfortunately, this library for service communication is made to be synchronous, and we want to parallelize its use.
In the following example (abstracted) code, the issue is that CallEndpointSynchronously unfortunately uses some built in authentication, which throws null exception when HttpContext isn't set:
public class MyController: ApiController
//...
[HttpPost]
public async Task<IHttpActionResult> DoIt(IEnumerable<int> inputs)
{
var tasks = inputs.Select(i =>
Task.Run(()=>
{
/* call some REST endpoints, pass some arguments, get the response from each.
The obvious answer (HttpContext.Current = parentContext) can't work because
there's some async code underneath (for whatever reasons), and that would cause it
to sometimes not return to the same thread, and basically abandon the Context,
again resulting in null */
var results = Some3rdPartyTool.CallEndpointSynchronously(MyRestEndpointConfig[i]);
return results;
});
var outcome = await Task.WhenAll(tasks);
// collect outcome, do something with it, render outputs...
}
Is there a cure for this?
We want to optimize for single requests, not interested in maximizing parallel users at this moment.
Unfortunately, this library for service communication is made to be synchronous, and we want to parallelize its use.
throws null exception when HttpContext isn't set:
The obvious answer (HttpContext.Current = parentContext) can't work because there's some async code underneath (for whatever reasons), and that would cause it to sometimes not return to the same thread, and basically abandon the Context, again resulting in null
There's an important part of your question in the example code comment. :)
Normally, HttpContext shouldn't be shared across threads. It's just not threadsafe at all. But you can set HttpContext.Current (for some reason), so you can choose to live dangerously.
The more insidious problem here is that the library has a synchronous API and is doing sync-over-async - but somehow without deadlocking (?). At this point, I must be honest and say the best approach is to fix the library: make the vendor fix it, or submit a PR, or just rewrite it if you have to.
However, there is a tiny chance that you can get this kinda sorta working by adding Even More Dangerous code.
So, here's the information you need to know:
ASP.NET (pre-Core) uses an AspNetSynchronizationContext. This context:
Ensures that only one thread runs in this context at a time.
Sets HttpContext.Current for any thread that is running in the context.
Now, you could capture the SynchronizationContext.Current and install it on the thread pool threads, but in addition to being Very Dangerous, it would not achieve your actual goal (parallelization), since the AspNetSynchronizationContext only allows one thread in at a time. The first portion of the 3rd-party code would be able to run in parallel, but anything queued to the AspNetSynchronizationContext would run one thread at a time.
So, the only way I can think of making this work is to use your own custom SynchronizationContext that resumes on the same thread, and set HttpContext.Current on that thread. I have an AsyncContext class that can be used for this:
[HttpPost]
public async Task<IHttpActionResult> DoIt(IEnumerable<int> inputs)
{
var context = HttpContext.Current;
var tasks = inputs.Select(i =>
Task.Run(() =>
AsyncContext.Run(() =>
{
HttpContext.Current = context;
var results = Some3rdPartyTool.CallEndpointSynchronously(MyRestEndpointConfig[i]);
return results;
})));
var outcome = await Task.WhenAll(tasks);
}
So for each input, a thread is grabbed from the thread pool (Task.Run), a custom single-threaded synchronization context is installed (AsyncContext.Run), HttpContext.Current is set, and then the code in question is run. This may or may not work; it depends on how exactly Some3rdPartyTool uses its SynchronizationContext and HttpContext.
Note that there are several bad practices in this solution:
Using Task.Run on ASP.NET.
Accessing the same HttpContext instance simultaneously from multiple threads.
Using AsyncContext.Run on ASP.NET.
Blocking on asynchronous code (done by AsyncContext.Run and also presumably Some3rdPartyTool.
In conclusion, I again recommend updating/rewriting/replacing Some3rdPartyTool. But this pile of hacks might work.

A second operation started on this context before a previous operation completed ASP NET CORE

I encounter many trouble with this error in an ASP Net CORE mvc project and I didn't find a way to solve it :
A second operation started on this context before a previous operation
completed. Any instance members are not guaranteed to be thread safe
It append on a non async function :
public Employee GetEmployeeByuserID(String userID)
{
return _applicationDBContext.Employee.Where(e => e.UserId.Equals(userID)).First();
}
I've try to make this function async but it just make the error append somewhere else.
The function is in a DAL with a dependency injection of _applicationDBContext.
The Dal is declared is declared in AddTransient in the startup.cs
I've seen some solution with an await but I counld'n find where I'm suppose to put it.
EDIT
If I made a breakpoint to pause the code before return _applicationDBContext.Employee.Where(e => e.UserId.Equals(userID)).First();
and continue, it work well.
I finally made it work by creating a new DAL each request :
Employee emp = new EmployeeDal(new ApplicationDbContext()).GetEmployeeByuserID(appUser.Id);
If you have a better solution, please let me know.

EF Core and big traffic leads to max pool size was reached error

We're using ASP.NET Entity Framework Core for querying our MSSQL database in our Web API app. Sometimes when we have big traffic, querying to DB ends with this error:
Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
I wonder if our pattern of using DbContext and querying is correct or if I am missing some using/dispose pattern and error is caused by some memory leak (after some research I read then I should not use using because the lifetime is managed by the framework). I am following documentation...
My connectionString:
"myConnection": "Server=xxx;Database=xxx;user id=xxx;password=xxx;Max Pool Size=200;Timeout=200;"
My Startup.cs
public void ConfigureServices(IServiceCollection services)
{
.....
// scoped context
services.AddDbContext<MyDbContext>(
options => options.UseSqlServer(this.Configuration.GetConnectionString("myConnection")));
}
then in controllers I used dbcontext by dependency injection:
public class MyController : Controller
public MyController (MyDbContext context)
{
this.Context = context;
}
public ActionResult Get(int id)
{
// querying
return this.Context.tRealty.Where(x=>x.id == id).FirstOrDefault();
}
Should I use something like:
using (var context = this.Context)
{
return this.Context.tRealty.Where(x => x.id == id).FirstOrDefault();
}
But I think that this is bad pattern when I am using dependency injection of DbContext.
I think problem was caused by storing objects from database context queries to In memory cache. I had one big LINQ query to database context with some other subqueries inside. I called FirstOrDefault() on the end of main query but not inside subqueries. Controller was fine with it, it materialize queries by default.
return this.Context.tRealty.AsNoTracking().Where(
x => x.Id == id && x.RealtyProcess == RealtyProcess.Visible).Select(
s => new
{ .....
// subquery
videos = s.TVideo.Where(video => video.RealtyId == id && video.IsPublicOnYouTube).
Select(video => video.YouTubeId).ToList()), // missing ToList()
.....
}).FirstOrDefault();
And there was problem - subqueries were holding connection to database context when they where storing to In memory cache. When I implemented Redis distributed cache, it was first failing on some strange errors. It helps when I write ToList() or FirstOrDefault() to all my subqueries because distributed cache needs materialized objects.
Now I have all my queries materialized explicitly and I got no max pool size was reached error. So that one must be careful when stored objects from database context queries to In memory cache. It is need to materialize all queries to avoid to holding connection somewhere in memory.
You can set the lifetime of the DbContext in your startup.cs, see if this helps:
services.AddDbContext<MyDbContext>(options => options
.UseSqlServer(connection), ServiceLifetime.Scoped);
Also if your query is a simple read you can remove tracking by using .AsNoTracking().
Another way to improve your throughput is to prevent locks by using a transaction block with IsolationLevel.ReadUncommitted for simple reads.
You can also use the Snapshot isolation level - which is slightly more restrictive - if you do not want dirty reads.
TransactionOptions transactionOptions = new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted};
using (TransactionScope transactionScope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
{
// insert magic here
}
Edit : As the author of the question mentioned, the above code is not (yet?) possible in EF Core.
A workaround can be found here using an explicit transaction:
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
// transaction.Commit();
// transaction.Rollback();
}
}
I have not tested this.
Edit 2: Another untested snippet where you can have executed commands to set isolation level:
using (var c1= new SqlConnection(connectionString))
{
c1.Open();
// set isolation level
Exec(c1, "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
Exec(c1, "BEGIN TRANSACTION;");
// do your magic here
}
With Exec:
private static void Exec(SqlConnection c, string s)
{
using (var m = c.CreateCommand())
{
m.CommandText = s;
m.ExecuteNonQuery();
}
}
Edit 3: According to that thread, Transactions will be supported from .NET Core version 1.2 onwards.
#mukundabrt this is tracked by dotnet/corefx#2949. Note that
TransactionScope has already been ported to .NET Core but will only be
available in .NET Core 1.2.
I am adding an alternative answer, in case anyone lands here with a slightly different root cause, as was the case for my .NET Core MVC application.
In my scenario, the application was producing these "timeout expired... max pool size was reached" errors due to mixed use of async/await and Task.Result within the same controller.
I had done this in an attempt to reuse code by calling a certain asynchronous method in my constructor to set a property. Since constructors do not allow asynchronous calls, I was forced to use Task.Result. However, I was using async Task<IActionResult> methods to await database calls within the same controller. We engaged Microsoft Support, and an Engineer helped explain why this happens:
Looks like we are making a blocking call to an Async method inside
[...] constructor.
...
So, basically something is going wrong in the call to above
highlighted async method and because of which all the threads listed
above are blocked.
Looking at the threads which are doing same operation and blocked:
...
85.71% of threads blocked (174 threads)
We should avoid mixing async and blocking code. Mixed async and
blocking code can cause deadlocks, more-complex error handling and
unexpected blocking of context threads.
https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
https://blogs.msdn.microsoft.com/jpsanders/2017/08/28/asp-net-do-not-use-task-result-in-main-context/
Action Plan
Please engage your application team to revisit the application code of above mentioned method to understand what is going
wrong.
Also, I would appreciate if you could update your application logic to
not mix async and blocking code. You could use await Task instead of
Task.Wait or Task.Result.
So in our case, I pulled the Task.Result out of the constructor and moved it into a private async method where we could await it. Then, since I only want it to run the task once per use of the controller, I store the result to that local property, and run the task from within that method only if the property value is null.
In my defense, I expected the compiler would at least throw a warning if mixing async and blocking code is so problematic. However, it seems obvious enough to me, in hindsight!
Hopefully, this helps someone...

Async/Await with Microsoft Unity

I have a project that uses Microsoft Unity and Web API 2. It works great and there are no problems with it. However when I try to use async/await
public async Task<IHttpActionResult> Post(PackageOrderDto dto)
{
try
{
newOrderNumber = await _apiPlaceOrder.Save(dto));
}
catch (ApiValidationFailedException apiValidationFailedException)
{
return BadRequest(apiValidationFailedException);
}
return Ok("VF" + newOrderNumber);
}
I get a ResolutionFailedException with this message:
Exception is:
InvalidOperationException - The PerRequestLifetimeManager can only be
used in the context of an HTTP request. Possible causes for this error
are using the lifetime manager on a non-ASP.NET application, or using
it in a thread that is not associated with the appropriate
synchronization context.
The ApiPlaceOrder is managed using UnityHierarchicalDependencyResolver.
The code works fine when async/await is not used.
Any ideas how to get Unity to play nice with async/await?
I've stripped the code for _apiPlaceOrder.Save(...) right back to this to try and isolate the problem and I still get the same issue:
public class ApiPlaceOrder
{
public async Task<int> Save(PackageOrderDto dto)
{
await Task.Delay(10000);
return 1;
}
}
You will get this error when you are using PerRequestLifetimeManager in the Unity.Mvc project and HttpContext.Current is null. That will occur when you are no longer on the originating thread of the http request.
Check your code for any occurrences of await fooTask.ConfigureAwait(false) or something that is creating a new thread or acquiring a thread from the thread pool. Hook up a debugger and put HttpContext.Current in your watch window and step through your code until that property switches to null.

#Html.Action is raising "HttpServerUtility.Execute blocked while waiting for an asynchronous operation to complete. " [duplicate]

Every time I try to use the new Async and Await operators and return a collection of objects from a database I get an Invalid Operation exception. When I use it to only return a single Item it works fine.
Controller Code:
public async Task<ActionResult> EnvironmentList()
{
EfEnvironmentDataAccess dataAccess = new EfEnvironmentDataAccess();
ICollection<Environment> environments = await dataAccess.GetAllEnvironmentsAsync();
return PartialView(environments);
}
View Code:
<div class="ECURightCol">
<h3>Table Dumps</h3>
#Html.Action("EnvironmentList", "Environment")
#Html.Action("ComputerList", "Computer")
#Html.Action("ProductList", "Product")
#Html.Action("InstanceList", "Instance")
#Html.Action("ProfileList", "Profile")
The Data Access Code:
public ICollection<Environment> GetAllEnvironments()
{
using (EcuWebDataContext db = new EcuWebDataContext())
{
return db.Environments.OrderBy(e => e.Name).ToList();
}
}
public async Task<ICollection<Environment>> GetAllEnvironmentsAsync()
{
return await Task.Run(() => GetAllEnvironments());
}
The Error I get is:
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: HttpServerUtility.Execute blocked while waiting for an asynchronous operation to complete.
First of all, you cannot use asynchronous processing with child actions and I suppose this is what you are trying to do.
Secondly, you are not doing any asynchronous processing here by spinning up another thread to execute your code with the below line of code:
Task.Run(() => GetAllEnvironments());
It will block a thread at the end of the day and you will have nothing but a context switch overhead. EF6 will have support for asynchronous processing. For asynchronous queries with pure ADO.NET, have a look:
Asynchronous Database Calls With Task-based Asynchronous Programming Model (TAP) in ASP.NET MVC 4
It's been a while since this was answered but another way around is as follows:
Call your method from an action
#Html.Action("YourSyncMethod", "YourController")
Define it as a normal sync action
public ActionResult YourSyncMethod()
Then inside it call your async method
var taskResponse = YourAsyncMethod();
which will return a model with whatever you need
private async Task<YourModel> YourAsyncMethod()
It seems simpler than tampering with config options or creating more complex code
It's been some time since this question was answered. But I was having a similar situation with MVC 5 and I was able to make a [ChildActionOnly] work asynchronously just by commenting out the following line under <system.web> section of web.config file.
<system.web>
<!--<httpRuntime targetFramework="4.5" />-->
EDIT:
Consider this a workaround while you find a real solution for your situation. Please see Leri's comments below.

Categories