我正在开发多租户应用程序。我为每个租户使用单独的数据库。 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);
}
答案 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;
}