Как передать UserId / TenantId в конструктор репозитория, используя инъекцию зависимостей?

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

Вопрос

Я пишу веб-службу с несколькими арендаторами, используя ASP.NET 5 и EF7. Данные репозитория относятся к арендатору / пользователю. Поскольку я буду использовать TenantId во всех вызовах, я бы хотел инициализировать репозиторий с помощью текущего 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>();
    }
}

Возможно ли инициализировать мой репозиторий один раз после аутентификации пользователя? Как передать TenantId с помощью конструктора?

Популярные ответы

И ваши идентификаторы арендатора, и идентификаторы пользователя - это данные времени выполнения, и вы не должны вводить данные времени выполнения в компоненты вашей системы (в этом случае ваш репозиторий), потому что это значительно усложняет ваш корневой состав (как вы уже это испытываете) и делает его практически невозможным для проверки графа объекта. Данные времени выполнения должны проходить через уже построенный графический объект. Есть два способа достижения этого. Либо вы передаете данные через вызовы методов общедоступного API вашего компонента, либо вы добавляете компонент, который отвечает за получение значения времени выполнения при его запросе.

В вашем случае последний вариант лучше. Поэтому мой совет заключается в том, чтобы внедрить компонент, который позволяет получить эту контекстуальную информацию:

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

Это объясняет проблему элегантно, потому что теперь вы можете определить реализацию ITenantContext которая знает, как получить правильный идентификатор арендатора для текущего запроса. Например:

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

Это даже позволяет регистрировать все ваши компоненты в качестве одноэлементного, что может повысить производительность и уменьшить изменение общих ошибок, таких как Captive Dependencies .



Related

Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему
Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему