Wie Übergeben der UserId / TenantId an einen Repository-Konstruktor, der Dependency Injection verwendet?

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

Frage

Ich schreibe einen Multi-Tenants-Webdienst mit ASP.NET 5 und EF7. Die Repository-Daten sind für einen Mandanten / Benutzer spezifisch. Da ich die TenantId in allen Aufrufen verwenden werde, möchte ich das Repository mit der aktuellen TenantId initialisieren.

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

Ist es möglich, mein Repository einmal zu initialisieren, nachdem ein Benutzer authentifiziert wurde? Wie würde ich die TenantId mit dem Konstruktor übergeben?

Beliebte Antwort

Sowohl Ihre Mandanten- als auch Benutzer-ID-Werte sind Laufzeitdaten und Sie sollten keine Laufzeitdaten in die Komponenten Ihres Systems (in diesem Fall Ihr Repository) einfügen, da dies Ihren Kompositionswurzel (wie Sie es bereits kennen) enorm erschwert und nahezu unmöglich macht um Ihr Objektdiagramm zu überprüfen. Laufzeitdaten sollten durch einen bereits erstellten Objektgraphen fließen. Es gibt grundsätzlich zwei Möglichkeiten, dies zu erreichen. Entweder Sie übergeben die Daten über die Methodenaufrufe der öffentlichen API Ihrer Komponente oder Sie injizieren eine Komponente, die für das Abrufen des Laufzeitwerts verantwortlich ist, wenn dieser angefordert wird.

In Ihrem Fall ist die letztere Option am besten. Mein Rat ist daher, eine Komponente zu injizieren, die es erlaubt, diese kontextuellen Informationen abzurufen:

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

Dies löst das Problem elegant, da Sie jetzt eine ITenantContext Implementierung definieren ITenantContext , die weiß, wie die richtige Mandanten-ID für die aktuelle Anforderung abgerufen werden kann. Zum Beispiel:

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

Dies ermöglicht Ihnen sogar, alle Ihre Komponenten als Singleton zu registrieren, was die Leistung verbessern und die Änderung gängiger DI-Fallstricke, z. B. Captive Dependencies , reduzieren kann.



Related

Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum