Come passare UserId / TenantId a un costruttore di repository utilizzando Iniezione di dipendenza?

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

Domanda

Sto scrivendo un servizio web multi-tenant usando ASP.NET 5 e EF7. I dati del repository sono specifici per un titolare / utente. Poiché userò il TenantId in tutte le chiamate, vorrei inizializzare il repository con l'attuale TenantId.

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

È possibile inizializzare il mio deposito una volta che un utente è stato autenticato? Come potrei passare il TenantId con il costruttore?

Risposta popolare

Sia il tuo tenant che i valori di id utente sono dati di runtime e non devi inserire dati di runtime nei componenti del tuo sistema (il tuo repository in questo caso), perché questo complica tremendamente il tuo Composition Root (come lo stai già sperimentando) e lo rende quasi impossibile per verificare il tuo oggetto grafico. I dati di runtime dovrebbero scorrere attraverso un grafico di oggetto già costruito. Ci sono fondamentalmente due modi per raggiungere questo obiettivo. O si passano i dati attraverso le chiamate al metodo dell'API pubblica del proprio componente o si inietta un componente responsabile del recupero del valore di runtime quando viene richiesto.

Nel tuo caso, quest'ultima opzione è la migliore. Il mio consiglio è quindi quello di iniettare un componente che consente di recuperare queste informazioni contestuali:

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

Questo risolve il problema elegantemente, perché ora è possibile definire un'implementazione ITenantContext che sa come recuperare l'ID tenant corretto per la richiesta corrente. Per esempio:

public sealed class AspNetSessionTenantContext : ITenantContext {
    public int CurrentTenantId {
        get { return (int)HttpContext.Current.Session["tenantId"]; }
    }
}

Ciò consente anche di registrare tutti i componenti come singleton, che può migliorare le prestazioni e ridurre il cambiamento delle comuni insidie ​​DI, come le dipendenze in cattività .



Related

Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché