如何使用依赖注入将UserId/TenantId传递给存储库构造函数?

Mar*_*tin 3 asp.net-mvc dependency-injection entity-framework-core asp.net-core-mvc asp.net-core

我正在使用ASP.NET 5和EF7编写多租户Web服务.存储库数据特定于租户/用户.因为我将在所有调用中使用TenantId,所以我想用当前的TenantId初始化Repository.

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>();
    }
}
Run Code Online (Sandbox Code Playgroud)

在用户通过身份验证后,是否可以初始化我的存储库一次?我如何通过构造函数传递TenantId?

Ste*_*ven 10

您的租户和用户ID值都是运行时数据,您不应该将运行时数据注入系统的组件(在这种情况下是您的存储库),因为这极大地使您的组合根复杂化(正如您已经遇到的那样)并使其几乎不可能验证您的对象图.运行时数据应该流经已构建的对象图.实现这一点基本上有两种方法.您可以通过组件的公共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();
}
Run Code Online (Sandbox Code Playgroud)

这样可以优雅地解决问题,因为现在您可以定义一个ITenantContext知道如何为当前请求检索正确的租户ID的实现.例如:

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

这甚至允许您将所有组件注册为单例,这可以提高性能,并减少常见DI陷阱的更改,例如强制依赖性.