У меня распространенная ошибка при использовании EntityFramework, но я не могу выяснить причину. Ошибка:
"Вторая операция началась в этом контексте перед завершением предыдущей операции. Обычно это вызвано тем, что разные потоки используют один и тот же экземпляр DbContext. Для получения дополнительной информации о том, как избежать проблем с многопоточностью DbContext, см. https://go.microsoft.com/fwlink/?linkid=2097913. "
Я использую промежуточное ПО GraphQL для обработки запросов.
Отслеживание стека:
at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
at Test.DataAccess.Intranet.Repository.Admin.UserModule.Query.GetUserDataQuery.Execute(String username) in C:\Projects\GIT Projects\SWDi\Intranet\Test.Backend\Test.DataAccess.Intranet\Repository\Modules\Admin\UserManagement\Query\Implementation\GetUserDataQuery.cs:line 34
at Test.Web.Base.Services.UserService.GetUserContext(HttpContext context) in C:\Projects\GIT Projects\Test\Intranet\Test.Backend\Test.Web.Base\Services\UserService.cs:line 55
at Test.API.Middleware.GraphQLMiddleware.InvokeAsync(HttpContext httpContext) in C:\Projects\GIT Projects\Test\Intranet\Test.Backend\Test.Web.API\Middleware\GraphQLMiddleware.cs:line 71
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Test.Web.API.Middleware.NtlmAndAnonymousSetupMiddleware.Invoke(HttpContext context) in C:\Projects\GIT Projects\Test\Intranet\SWDi.Backend\Test.Web.API\Middleware\NtlmAndAnonymousSetupMiddleware.cs:line 39
at Test.API.Middleware.ExceptionMiddleware.Invoke(HttpContext context) in C:\Projects\GIT Projects\Test\Intranet\Test.Backend\Test.Web.API\Middleware\ExceptionMiddleware.cs:line 40"
Здесь я обрабатываю POST-запросы в GraphQLMiddleware.cs:
public async Task InvokeAsync(HttpContext httpContext)
{
if (
(httpContext.Request.Path.StartsWithSegments(CATCHURLPATH) || httpContext.Request.PathBase.StartsWithSegments(CATCHURLPATH))
&& string.Equals(httpContext.Request.Method, CATCHURLMETHOD, StringComparison.OrdinalIgnoreCase))
{
string body;
var userContext = await this.userService.GetUserContext(httpContext);
using (var streamReader = new StreamReader(httpContext.Request.Body))
{
body = await streamReader.ReadToEndAsync();
var request = JsonConvert.DeserializeObject<GraphQLQueryBase>(body);
var result = await this.executor.ExecuteAsync(doc =>
{
doc.ThrowOnUnhandledException = true;
doc.Schema = this.schema;
doc.Query = request.Query;
doc.Inputs = request.Variables.ToInputs();
doc.ExposeExceptions = true;
doc.UserContext = new Dictionary<string, object>()
{
{ "UserId", userContext.UserId },
{ "Username", userContext.Username },
};
}).ConfigureAwait(false);
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Factory.StartNew(() => this.queryLogMutation.Execute(userContext.UserId, request.Query));
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
httpContext.Response.ContentType = "application/json";
await this.writer.WriteAsync(httpContext.Response.Body, result);
}
}
else
{
await this.next(httpContext);
}
}
Как видите, я звоню на userService.GetUserContext
метод, который реализован как
public class UserService : IUserService
{
private const string CACHEKEY = "userContext_";
private readonly IMemoryCache cache;
private readonly IGetUserDataQuery getUserByUsernameQuery;
/// <summary>
/// Initializes a new instance of the <see cref="UserService"/> class.
/// </summary>
/// <param name="cache">Get the cache from DI.</param>
/// <param name="getUserByUsernameQuery">The Data Repository from DI.</param>
public UserService(IMemoryCache cache, IGetUserDataQuery getUserByUsernameQuery)
{
this.cache = cache;
this.getUserByUsernameQuery = getUserByUsernameQuery;
}
/// <summary>
/// Created the user context information.
/// </summary>
/// <param name="context">The current Httpcontext.</param>
/// <returns>The user context.</returns>
public async Task<UserContext> GetUserContext(HttpContext context)
{
UserContext returnContext;
var username = this.GetUsername(context);
if (!this.cache.TryGetValue(CACHEKEY + username, out returnContext))
{
var dbuser = await this.getUserByUsernameQuery.Execute(username);
if (dbuser == null)
{
throw new NullReferenceException("Username " + username + " not found in user table. Please run AD update or insert user manually.");
}
returnContext = new UserContext()
{
Username = username,
UserContextObject = context.User,
UserId = dbuser.UserId,
};
this.cache.Set(CACHEKEY + username, returnContext);
}
return returnContext;
}
/// <summary>
/// Splits the username out of a Domain//Username.
/// </summary>
/// <param name="context">The current Http Context.</param>
/// <returns>The username of the given http context identity.</returns>
internal string GetUsername(HttpContext context)
{
return context.User.Identities.First().Name.Split("\\")[1];
}
}
И, наконец, запрос getUserByUsername
:
public class GetUserDataQuery : IGetUserDataQuery
{
/// <summary>
/// Initializes a new instance of the <see cref="GetUserDataQuery"/> class.
/// </summary>
/// <param name="context">Intranet Context by DI.</param>
public GetUserDataQuery(IntranetContext context)
{
this.Context = context;
}
private IntranetContext Context { get; }
/// <summary>
/// Get the Intranet user context using the users (AD) username.
/// </summary>
/// <param name="username">The username.</param>
/// <returns>The DB user context.</returns>
public async Task<MainUserModel> Execute(string username)
{
var userInformation = await this.Context.VUser.Where(x => x.Username == username).SingleOrDefaultAsync();
var userRights = await this.Context.UserModule.Where(x => x.UserId == userInformation.UserId).Include(x => x.RefModule).ToListAsync();
var userFunctions = await this.Context.UserModuleFunction.Where(x => x.UserId == userInformation.UserId).Include(x => x.RefModuleFunction).ToListAsync();
var memberList = await this.Context.UserGroupMember.Where(x => x.UserId == userInformation.UserId).Include(x => x.UserGroup).ToListAsync();
var isCashier = await this.Context.UserCashbox.Where(x => x.UserId == userInformation.UserId).AnyAsync();
return new MainUserModel(userInformation, userFunctions, userRights, memberList, isCashier);
}
}
Несмотря на то, что я сделал dbContext
кратковременным, проверил на наличие забытых асинхронных / ожидающих ключевых слов или даже вложенные асинхронные сообщения, ошибка все еще происходит. Если вам нужна дополнительная информация или есть идеи, пожалуйста, дайте мне знать.