Cómo obtener un Usuario en la Capa de Servicio

Uso ASP.NET Core 2.1 y me gustaría obtenerUser en unnivel de servici.

He visto ejemplos cuandoHttpContextAccessor se inyecta en algún servicio y luego buscamos el @ actuUser viaUserManager

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

o en el controlador

var user = await _userManager.GetUserAsync(User);

Problemas

InyecciónHttpContextAccessor en servicio parece serincorrect - simplemente porque violamos SRP y laService Layer no está aislado (depende dehttp context).

Podemos, por supuesto,fetch usuario en un controlador (un enfoque algo mejor), pero nos enfrentamos a un dilema: simplemente no queremos pasarUser como parámetro en cada método de servicio individual

Pasé unas horas pensando en la mejor manera de implementarlo y se me ocurrió una solución. Simplemente no estoy completamente seguro de que mi enfoque sea adecuado y no viole ninguno de los principios de diseño de software.

Compartir mi código con la esperanza de obtener recomendaciones de la comunidad StackOverflow.

La idea es la siguiente

Primero, presentoSessionProvider que está registrado como Singleton.

services.AddSingleton<SessionProvider>();

SessionProvider tiene unSession propiedad que contieneUser, Tenant, etc.

n segundo lugar, presento aSessionMiddleware y regístralo

app.UseMiddleware<SessionMiddleware>();

En elInvoke método resuelvoHttpContext, SessionProvider & UserManager.

I fetchUser

Entonces inicializoSession propiedad deServiceProvider singleton:

sessionProvider.Initialise(user);

En este puntoServiceProvider tieneSession objeto que contiene la información que necesitamos.

Ahora inyectamosSessionProvider en cualquier servicio y suSession objeto está listo para usar.

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);
        }
    }
}

Y ahoraService Layer código:

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

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

Así que esta es labas class para cualquier servicio, como puede ver ahora podemos obtenerSession objeto fácilmente y está listo para usar:

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);
    }
}

Céntrate en el siguiente bit:

.GetAll(Session.TenantId.Value);

también, podemos obtener fácilmente el usuario actual

Session.UserId.Value

Session.User

Eso es todo

Probé mi código y funciona bien cuando hay varias pestañas abiertas: cada pestaña tiene un subdominio diferente en la URL (el inquilino se resuelve desde el subdominio; los datos se obtienen correctamente).

Respuestas a la pregunta(3)

Su respuesta a la pregunta