MVC 6(vNext)UserManager - DI - 在特定数据库中创建用户

时间:2016-01-03 15:55:27

标签: asp.net-mvc asp.net-identity asp.net-core-mvc asp.net-core

我正在开发多租户应用程序。我为每个租户使用单独的数据库。 UserData适用于单独数据库TOO中的每个租户。

我的问题是如何在" custom"中为每个租户创建管理员帐户?数据库独立于DI。在MVC 5中,可以基于UserStore(连接字符串)实例化UserManager。但mvc6中的UserManager依赖于HttpContext ...没有找到文档... 请以某种方式存在怎么办?在mvc 5中我需要mvc 6这样的东西:

 UserStore<TenantUser> store = new UserStore<TenantUser>(new TenantDbContext("CONNECTION STRING"));  //!!!  NO POSSIBLE CREATE USER IN CUSTOM DATABASE
        UserManager<TenantUser> t = new UserManager<TenantUser>(store);
        t.CreateAsync(user, password);

更新

public class TenantDbContext : IdentityDbContext<TenantUser, TenantRole, Guid>
{
    private string _connectionString { get; set; }
    private readonly IHttpContextAccessor _contextAccessor;
    private readonly ApplicationDbContext _applicationDbContext;

    //THIS SUB UNCOMENT ONLY IF CREATE MIGRATIONS (dnx ef...)
    /*  
    public TenantDbContext(DbContextOptions<TenantDbContext> options) : base(options) 
    {
        this._connectionString = "CONNECTION STRING";
    }
    */
    public TenantDbContext(DbContextOptions<TenantDbContext> options, IHttpContextAccessor contextAccessor, ApplicationDbContext applicationDbContext) : base(options)          {                            
        _contextAccessor = contextAccessor;
        _applicationDbContext = applicationDbContext;
        TenantResolver resolver = new TenantResolver(_contextAccessor, _applicationDbContext);
        string con = resolver.GetConnectionString();
        if (con != string.Empty)
        {
            this._connectionString = con;                            }
        else
        {
            this._connectionString = "CONNECTION STRING"; //Development connection string
        }
    }

    public TenantDbContext()        //Posibility to create TenantDbContext migration and development database with no connectionString in constructor
    {
        //this._connectionString = "CONNECTION STRING";
    }   
    public TenantDbContext(string ConnectionString)
    {
        this._connectionString = ConnectionString;
    }
    public static TenantDbContext Create(string ConnectionString)
    {
        return new TenantDbContext(ConnectionString);
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    { 
        optionsBuilder.UseSqlServer(_connectionString);  
    }

2 个答案:

答案 0 :(得分:1)

我希望我能正确理解你。让我们忘记已经打开到数据库的多个连接的性能和缓存。我想你有多个数据库,它们具有相同的模式。您需要使用(共享)相同的数据库上下文来访问数据库。

我可以建议你两个解决方案。

第一个解决方案包括注册一个上下文和重新打开,如果打开的那个更改了连接字符串。

让我们拥有TenantDbContext,可以使用不同的目标数据库打开它。例如

@"Server=(localdb)\mssqllocaldb;Database=TenantDb1;Trusted_Connection=True;"

@"Server=(localdb)\mssqllocaldb;Database=TenantDb2;Trusted_Connection=True;"

首先,您删除OnConfiguring之类的

protected override void OnConfiguring(DbContextOptionsBuilder options)
{
    options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=TenantDb;Trusted_Connection=True;");
}

可能存在于TenantDbContext的定义中,您在ConfigureServices的{​​{1}}中使用以下代码:

Startup.cs

public void ConfigureServices(IServiceCollection services) { // Add framework services. var connection1 = @"Server=(localdb)\mssqllocaldb;Database=TenantDb1;Trusted_Connection=True;"; services.AddEntityFramework() .AddSqlServer() .AddDbContext<TenantDbContext>(options => options.UseSqlServer(connection1)); services.AddMvc(); ... } 注入数据库(TenantDbContext)的方式。我们TenantDb1包含一些实体集,例如TenantDbContext。因此,您可以通过以下方式定义一些MVC控制器

Blog

第二个解决方案不需要使用public class TenantsController : Controller { private TenantDbContext _context; public TenantsController (TenantDbContext context) { _context = context; } public IActionResult Index() { var con = _context.Database.GetDbConnection(); // now the con uses either TenantDb2 or TenantDb2 // con.ConnectionString can be used to get or set the // connection string string needConStr = @"Server=(localdb)\mssqllocaldb;Database=TenantDb2;Trusted_Connection=True;"; if (con.ConnectionString != needConStr) { // can be compared more carefully _context.Database.CloseConnection(); con.ConnectionString = needConStr; } // get some data from the TenantDbContext var blog = _context.Blog.ToList(); return View(blog); } } 注入任何TenantDbContext。而不是只需添加一个简单的构造函数到DependencyInjection

TenantDbContext

这种简单的构造函数允许您在需要时随时创建上下文:

public TenantDbContext(DbContextOptions optionsBuilder): base (optionsBuilder)
{
}

我在代码中直接使用了所有连接字符串。您可以轻松修改上述代码,以从配置文件public class TenantsController : Controller { public IActionResult Index() { var contextOptions = new DbContextOptionsBuilder(); contextOptions.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=TenantDb2;Trusted_Connection=True;"); var context = new BloggingContext(contextOptions.Options); context.Database.OpenConnection(); // get some data from the TenantDbContext var blog = context.Blog.ToList(); return View(blog); } } 获取所有连接字符串。

答案 1 :(得分:0)

解决。 1. CreateCustomUserStore

public class TenantUserStore : UserStore<TenantUser, TenantRole, TenantDbContext, Guid>
{
    public TenantUserStore(TenantDbContext context, IdentityErrorDescriber describer = null): base(context, describer)
    {  
    }
}

以下是如何使用自定义数据库实例化UserManager的代码:

IUserStore<TenantUser> CustomStore = new TenantUserStore(new TenantDbContext(coonection), null);    

UserManager<TenantUser> manager = new UserManager<TenantUser>(CustomStore, _optionsAccessor, _passwordHasher, _userValidators,
                    _passwordValidators, _keyNormalizer, _errors, _services, _logger, _contextAccessor);

DI仅用于UserManager构造函数的其余部分:

       public class TenantsController : Controller
{
    private readonly IHttpContextAccessor _contextAccessor;        
    private readonly IOptions<IdentityOptions> _optionsAccessor;
    private readonly IPasswordHasher<TenantUser> _passwordHasher;
    private readonly IEnumerable<IUserValidator<TenantUser>> _userValidators;
    private readonly IEnumerable<IPasswordValidator<TenantUser>> _passwordValidators;
    private readonly ILookupNormalizer _keyNormalizer;
    private readonly IdentityErrorDescriber _errors;
    private readonly IServiceProvider _services;
    private readonly ILogger<UserManager<TenantUser>> _logger;


    public TenantsController(IHttpContextAccessor contextAccessor, 
         IOptions<IdentityOptions> optionsAccessor,
         IPasswordHasher<TenantUser> passwordHasher,
         IEnumerable<IUserValidator<TenantUser>> userValidators,
         IEnumerable<IPasswordValidator<TenantUser>> passwordValidators,
         ILookupNormalizer keyNormalizer,
         IdentityErrorDescriber errors,
         IServiceProvider services,
         ILogger<UserManager<TenantUser>> logger


        )
    {
         _optionsAccessor = optionsAccessor;
        _passwordHasher = passwordHasher;
        _userValidators = userValidators;
        _passwordValidators = passwordValidators;
        _keyNormalizer = keyNormalizer;
        _errors = errors;
        _services = services;
        _logger = logger;            
        _contextAccessor = contextAccessor;
    }