Ho l'autenticazione jwt:
var messageHandlers = new JwtMessageHandler(_serviceProvider);
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
Events = new JwtBearerEvents
{
OnMessageReceived = messageHandlers.OnMessageReceived,
},
TokenValidationParameters = tokenValidationParameters
});
JwtMessageHandler
è il mio gestore personalizzato. Nel gestore devo fare alcune query al database, quindi passo a ServiceProvider
e risolvo il mio servizio utente:
public class JwtMessageHandler
{
private IUserService _userService;
public async Task OnMessageReceived(MessageReceivedContext arg)
{
//parsing header, get claims from token
...
_userService = (IUserService)arg.HttpContext.RequestServices.GetService(typeof(IUserService));
var isRoleChanged = await _userService.IsRoleChanged(tokenObject.Subject, rolesFromToken);
if (isRoleChanged)
{
GenerateBadResponse(arg);
return;
}
var canLogin = await _userService.CanLogin(tokenObject.Subject);
if (!canLogin)
{
GenerateBadResponse(arg);
return;
}
}
}
Nel servizio faccio domande:
...
var user = await _userManager.FindByEmailAsync(email);
var currentRoles = await _userManager.GetRolesAsync(user);
..
L' OnMessageReceived
viene chiamato per ogni richiesta. Quando ho una richiesta sulla pagina per il server o aspetto uno o due secondi prima di fare qualcosa, tutto funziona correttamente. Ma, ho diverse pagine in cui faccio 2-3 richieste simultanee al server. E, in questo caso, ho un errore su:
La connessione non è stata chiusa. Lo stato attuale della connessione è in connessione
Capisco quel problema con il multithreading. JwtMessageHandler
viene creato una volta all'avvio dell'applicazione. Quindi, ho messo la linea:
_userService = (IUserService)_serviceProvider.GetService(typeof(IUserService));
metodo interno, prima che si trovasse nel costruttore. Ma non ha aiutato. Inoltre, ho provato a impostare null su _userService
alla fine del mio metodo.
Come usare correttamente in questo caso?
Cercando di utilizzare una connessione che è già "in connessione" - segno chiaro di alcune condizioni di gara.
IUserService
è registrato con durata "scope" e anche tutte le dipendenze (userManager, dbContext) IServiceProvider
ottenuto durante l'avvio dell'app per la risoluzione dei servizi IServiceProvider
su scope, NON è correlato all'ambito di richiesta corrente e restituisce istanze da "un altro universo". Utilizzare HttpContext.RequestServices
per la risoluzione del servizio. JwtMessageHandler
è una / singola per app. Quindi non usare la sua proprietà per la memorizzazione di _userService
(rimuovere private IUserService _userService
). Utilizzare invece la variabile locale all'interno di OnMessageReceived
( var _userService = ...
). Hai già controllato (1), (2) e (3). Penso che (4) è l'ultimo che è necessario per correggere il bug.
Ho affrontato molto questa situazione. Ho sempre ottenuto utilizzando la parola chiave lock.
lock (_context)
{
var user = _context.users.first(x => x.Id == userId);
}
Questo blocca l'uso dell'oggetto (cioè _context) per il thread corrente e nessun altro thread può accedere contemporaneamente a questa stessa istanza, quindi nessun problema.