Odata [EnableQuery] attribute makes EF Core crash on high requests amount

Open
#24,121 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
30/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Stale
Tech stack
csharp
Domain
api, backend, database

Research direction

Start with the OfficeDtoController.GetOfficeDto entry point and the GetOffices method shown in the issue. Reproduce the example request with $count under concurrent load, comparing it with the request without $count. Done means identifying and resolving the concurrent DbContext operation without regressing the OData query behavior.

Written by the indexing model from the issue text.

Description

area-external customer-reported

I use ASP.NET Wen API on NET 5 and the following NuGets:
Microsoft.AspNetCore.OData => 7.5.4
Microsoft.Data.OData => 5.8.4
Microsoft.OData.Core => 7.8.1
Microsoft.EntityFrameworkCore => 5.0.2

(I have tried with Microsoft.AspNetCore.OData 8.0.0-preview3 and the problem is the same)

#23891 - How the problem started. But this is a different problem so I decided to post a new issue.

In my Odata controller I Use [EnableQuery(EnsureStableOrdering = false)]. Stable ordering is because of some custom Order by which I need in my code. Example in the mentioned Issue above

The problem however is with the [EnableQuery] attribute in general. When I have this attribute in the controller on high load, which means more than 150 request in a minute at the same endpoint I get a lot of the following exception:

"System.InvalidOperationException","Message":"A second operation was started on this context before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext."

"StackTraceString":" at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()\r\n at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable1.Enumerator.MoveNext()\r\n at System.Linq.Enumerable.Single[TSource](IEnumerable1 source)","RemoteStackTraceString":null,"RemoteStackIndex":0,"ExceptionMethod":null,"HResult":-2146233079,"Source":"Microsoft.EntityFrameworkCore"

I have used testing tool which send that kind of request to my endpoind (Result is around 240 requests in a minute with 1s delay between each) and most of the requests succeed, like maybe around 85% - 90% of them, however the failed requests get the exception above.

I needed some time to figure out what is happening, but when I remove the [EnableQuery] attribute everything was fine. No exception on any level of requests amount.

Here some code:


	public class OfficeDtoController : ODataController
	{
		private readonly IOfficesService _officesService;
		public OfficeDtoController(IOfficesService officesService)
		{
			_officesService = officesService;
		}

		[HttpGet]
		[EnableQuery(EnsureStableOrdering = false)]
		public IActionResult GetOfficeDto(ODataQueryOptions<OfficeDto> queryOptions, Guid? id = null, string sortColumn = "", string sortOrder = "", int? top = null, int? skip = null)
		{
			IQueryable<OfficeDto> officeDto = _officesService.GetOffices(id, sortColumn, sortOrder, top, skip);
			return Ok(officeDto);
		}

And the GetOffices method:

public IQueryable<OfficeDto> GetOffices(Guid? id, string sortColumn, string sortOrder, int? top, int? skip)
{
	IQueryable<Office> query = dbSet;
	query = query
	.Where(z => !z.Deleted);

	var list = query.Select(x =>
		new OfficeDto()
		{
			Id = x.Id,
			ZipCode = x.ZipCode,
			Active = x..Active,
			Employees = x.XrefOfficesEmployees
				.OrderBy(y => y.Order)
				.Select(y => new IdAndName()
				{
					Value = y.EmployeeId,
					Text = y.Employee.FirstName + " " + y.Employee.LastName,
    				Order = y.Order
    			})
    		});
    
	switch (sortColumn)
	{
		case "employees":
			if (sortOrder == "desc")
			{
				officeDto = officeDto.OrderByDescending(b => b.Employees.Select(a => a.Text).FirstOrDefault( ));
    		}
    		else
    		{
    			officeDto = officeDto.OrderBy(b => b.Employees.Select(a => a.Text).FirstOrDefault( ));
    		}
	}
		
	return list;
}

Here is the example URL:

api/OfficeDto?$count=true&$top=20&$skip=0&$select=id,zipcode,employees&$filter=(active eq true)

The exception I get assumes there is some wrong async/await usage, but as it can be seen this is Odata GET request and I do not use async/await anywhere. Maybe there is problem in the communication between EF Core and Odata.

EDIT:

I have found out that without $count it works fine. $Count makes second query to the DB apart from the $Select so I guess this is the problem. Any toughts?

Dominant language
C#
Stars
14.8k
Forks
3.4k
Avg merge
2d 5h
Merged PRs (30d)
134

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from dotnet/efcore

All issues in dotnet/efcore

Similar issues

More C# issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.