¿Cómo pasar el ID de usuario / TenantId a un constructor de repositorio utilizando la inyección de dependencia?

asp.net-core asp.net-core-mvc asp.net-mvc dependency-injection entity-framework-core

Pregunta

Estoy escribiendo un servicio web multi-tenants usando ASP.NET 5 y EF7. Los datos del repositorio son específicos de un inquilino / usuario. Debido a que usaré TenantId en todas las llamadas, me gustaría inicializar el Repositorio con el TenantId actual.

public class MyRepository {
    private int tenantId;
    public MyRepository(MyDbContext context, int tenantId) { 
        this.tenantId = tenantId;
        // ...
    }

    public Task<List<Data>> GetAllAsync() {
        return this.context.Data.Where(d => d.TenantId == this.tenantId).ToListAsync();
    }
}

public class Startup {
    public void ConfigureServices(IServiceCollection services) {
        // How do I pass the TenantId to the constructor?
        services.AddScoped<MyRepository>();
    }
}

¿Es posible inicializar mi Repositorio una vez después de que un usuario haya sido autenticado? ¿Cómo pasaría el TenantId con el constructor?

Respuesta popular

Tanto el inquilino como los valores de identificación de usuario son datos de tiempo de ejecución y no debe inyectar datos de tiempo de ejecución en los componentes de su sistema (su repositorio en este caso), ya que esto complica enormemente su raíz de composición (como ya lo está experimentando) y lo hace casi imposible para verificar su objeto grafico. Los datos de tiempo de ejecución deben fluir a través de un gráfico de objeto ya construido. Hay básicamente dos formas de lograr esto. O pasa a través de los datos a través de las llamadas de método de la API pública de su componente, o inyecta un componente que es responsable de recuperar el valor de tiempo de ejecución cuando se solicita.

En tu caso, la última opción es la mejor. Por lo tanto, mi consejo es inyectar un componente que permita recuperar esta información contextual:

public interface ITenantContext
{
    int CurrentTenantId { get; }
}

public interface IUserContext
{
    int CurrentUserId { get; }
}

public class MyRepository {
    private readonly Func<MyDbContext> contextProvider;
    private readonly ITenantContext tentantContext;

    public MyRepository(Func<MyDbContext> contextProvider, ITenantContext tentantContext){ 
        this.contextProvider = contextProvider;
        this.tentantContex = tentantContex;
    }

    public Task<List<Data>> GetAllAsync() {
        return this.contextProvider().Data
            .Where(d => d.TenantId == this.tenantContext.CurrentTenantId)
            .ToListAsync();
}

Esto resuelve el problema con elegancia, porque ahora puede definir una implementación de ITenantContext que sepa cómo recuperar el ID de inquilino correcto para la solicitud actual. Por ejemplo:

public interface ITenantContext
{
    int CurrentTenantId { get; }
}

public interface IUserContext
{
    int CurrentUserId { get; }
}

public class MyRepository {
    private readonly Func<MyDbContext> contextProvider;
    private readonly ITenantContext tentantContext;

    public MyRepository(Func<MyDbContext> contextProvider, ITenantContext tentantContext){ 
        this.contextProvider = contextProvider;
        this.tentantContex = tentantContex;
    }

    public Task<List<Data>> GetAllAsync() {
        return this.contextProvider().Data
            .Where(d => d.TenantId == this.tenantContext.CurrentTenantId)
            .ToListAsync();
}

Esto incluso le permite registrar todos sus componentes como singleton, lo que puede mejorar el rendimiento y reducir el cambio de las trampas comunes de la DI, como las dependencias cautivas .




Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué