IoC DI for multitenant DbContext with .Net Core Identity

时间:2017-09-26 07:43:58

标签: c# entity-framework dependency-injection .net-core inversion-of-control

我有.Net Core应用程序,并希望使用.Net Core Identity为DbContext设置IoC / DI:

public class DataContext : IdentityDbContext<User, IdentityRole<int>, int, IdentityUserClaim<int>, IdentityUserRole<int>, IdentityUserLogin<int>, IdentityRoleClaim<int>, IdentityUserToken<int>>, IDataContext
{
    private readonly string _dbName;

    public DataContext(string dbName)
    {
        this._dbName = dbName;
    }

    public virtual DbSet<Container> Containers { get; set; }

    public virtual DbSet<Note> Notes { get; set; }

    public virtual DbSet<Post> Posts { get; set; }

    public virtual DbSet<Email> Emails { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(ConnectionStrings.DataContext.Replace(":dbname", this._dbName));
        base.OnConfiguring(optionsBuilder);
    }
}

接口:

public interface IDataContext
{
    DbSet<Container> Containers { get; set; }

    DbSet<Note> Notes { get; set; }

    DbSet<Post> Posts { get; set; }

    DbSet<Email> Emails { get; set; }
}

Startup.cs我有以下几行:

services.AddDbContext<DataContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DataContext")));

但是该连接字符串不包含数据库名称。

我是否应该在接口这样的表中包含:AspNetUsers,AspNetRoles,......?

我想写Startup.cs类似的东西:

services.AddScoped<IDataContext, DataContext>();

使用界面而不是整个班级。

我能这样做吗?我怎么能这样做?

1 个答案:

答案 0 :(得分:3)

当您使用Entity Framework的AddDbContext方法时,它会为您注册服务集合中的上下文。

 services.AddDbContext<DataContext>(ServiceLifetime.Scoped);

但您可以像下面那样注册您的界面。

services.AddScoped(typeof(IDataContext), provider => provider.GetService<DataContext>());

然后您可以使用界面。但身份服务仍将使用具体类型。

PS:你知道repository pattern吗?它使您可以轻松地从服务层分离数据层。这对单元测试有很大帮助。

DataContext构造函数的问题在于依赖项容器不知道如何解析构造函数中的字符串。通常,当您在asp.net核心中使用配置时,可以使用IOptions类型及其基础结构。

在启动时,您可以添加选项并进行配置。

public class DatabaseOptions
{
    public string DbName { get; set; }
}

services.AddOptions();
services.Configure<DatabaseOptions>(options => options.DbName = "myDbName");

然后您可以将DataContext的构造更改为以下内容。

public DataContext(IOptions<DatabaseOptions> options)

如果您在启动时不知道配置值,则可以编写在中间件或操作过滤器中配置的上下文类型。

public class DbConfigurationContext
{
    public string DbName { get; set; }
}

public class DbConfigurationContextFilter : IAsyncActionFilter
{
    private readonly DbConfigurationContext _dbConfiguration;

    public DbConfigurationContextFilter(DbConfigurationContext dbConfiguration)
    {
        _dbConfiguration = dbConfiguration;
    }

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        _dbConfiguration.DbName = context.HttpContext.Request.Query["DbName"];
        await next();
    }
}


services.AddTransient<DbConfigurationContext>();
services.AddTransient<DbConfigurationContextFilter>();
services.AddMvc(setup =>
{
    setup.Filters.AddService(typeof(DbConfigurationContextFilter));
})

然后您可以在DbConfigurationContext中解决此问题DbContext。你也可以使用界面。

public DataContext(DbConfigurationContext configurationContext)