Como obter usuário na camada de serviço

Eu uso o ASP.NET Core 2.1 e gostaria de buscarUser em umNível de serviço.

Eu vi exemplos quandoHttpContextAccessor é injetado em algum serviço e, em seguida, buscamos a correnteUser através daUserManager

var user = await _userManager.GetUserAsync(accessor.HttpContext.User);

ou no controlador

var user = await _userManager.GetUserAsync(User);

Problemas:

InjetandoHttpContextAccessor em serviço parece sererrado - simplesmente porque violamos o SRP e oCamada de serviço não está isolado (depende decontexto http)

Podemos, é clarobuscar usuário em um controlador (uma abordagem um pouco melhor), mas enfrentamos um dilema - simplesmente não queremos passarUser como parâmetro em todos os métodos de serviço

Passei algumas horas pensando na melhor forma de implementá-lo e encontrei uma solução. Só não tenho certeza de que minha abordagem seja adequada e não viole nenhum dos princípios de design de software.

Compartilhando meu código na esperança de obter recomendações da comunidade StackOverflow.

A ideia é a seguinte:

Primeiro, eu apresentoSessionProvider que é registrado como Singleton.

services.AddSingleton<SessionProvider>();

SessionProvider tem umSession propriedade que detémUser, Tenant, etc.

Em segundo lugar, eu apresentoSessionMiddleware e registre-o

app.UseMiddleware<SessionMiddleware>();

NoInvoke método que eu resolvoHttpContext, SessionProvider & UserManager.

Eu buscoUser

Então eu inicializoSession propriedade deServiceProvider singleton:

sessionProvider.Initialise(user);

Nesta faseServiceProvider temSession objeto contendo as informações que precisamos.

Agora injetamosSessionProvider em qualquer serviço e suaSession O objeto está pronto para uso.

Código:

SessionProvider:

public class SessionProvider
{
    public Session Session;

    public SessionProvider()
    {
        Session = new Session();
    }

    public void Initialise(ApplicationUser user)
    {
        Session.User = user;
        Session.UserId = user.Id;
        Session.Tenant = user.Tenant;
        Session.TenantId = user.TenantId;
        Session.Subdomain = user.Tenant.HostName;
    }
}

Session:

public class Session
{
    public ApplicationUser User { get; set; }

    public Tenant Tenant { get; set; }

    public long? UserId { get; set; }

    public int? TenantId { get; set; }

    public string Subdomain { get; set; }
}

SessionMiddleware:

public class SessionMiddleware
{
    private readonly RequestDelegate next;

    public SessionMiddleware(RequestDelegate next)
    {
        this.next = next ?? throw new ArgumentNullException(nameof(next));
    }

    public async Task Invoke(
        HttpContext context,
        SessionProvider sessionProvider,
        MultiTenancyUserManager<ApplicationUser> userManager
        )
    {
        await next(context);

        var user = await userManager.GetUserAsync(context.User);

        if (user != null)
        {
            sessionProvider.Initialise(user);
        }
    }
}

E agoraCamada de serviço código:

public class BaseService
{
    public readonly AppDbContext Context;
    public Session Session;

    public BaseService(
        AppDbContext context,
        SessionProvider sessionProvider
        )
    {
        Context = context;
        Session = sessionProvider.Session;
    }
}

Então este é obase classe para qualquer serviço, como você pode ver, agora podemos buscarSession objeto facilmente e está pronto para uso:

public class VocabularyService : BaseService, IVocabularyService
{
    private readonly IVocabularyHighPerformanceService _vocabularyHighPerformanceService;
    private readonly IMapper _mapper;

    public VocabularyService(
        AppDbContext context,
        IVocabularyHighPerformanceService vocabularyHighPerformanceService,
        SessionProvider sessionProvider,
        IMapper mapper
        ) : base(
              context,
              sessionProvider
              )
    {
        _vocabularyHighPerformanceService = vocabularyHighPerformanceService;
        _mapper = mapper; 
    }

    public async Task<List<VocabularyDto>> GetAll()
    {
        List<VocabularyDto> dtos = _vocabularyHighPerformanceService.GetAll(Session.TenantId.Value);
        dtos = dtos.OrderBy(x => x.Name).ToList();
        return await Task.FromResult(dtos);
    }
}

Concentre-se no seguinte bit:

.GetAll(Session.TenantId.Value);

Além disso, podemos facilmente obter usuários atuais

Session.UserId.Value

ou

Session.User

Então é isso.

Testei meu código e ele funciona bem quando várias guias estão abertas - cada guia possui um subdomínio diferente na URL (o inquilino é resolvido a partir do subdomínio - os dados estão sendo buscados corretamente).

questionAnswers(3)

yourAnswerToTheQuestion