我目前正与Ben Fosters Saaskit打转道。
我使用AppTenantId属性扩展了ApplicationUser
并创建了一个自定义UserStore,它使用AppTenant来识别用户:
public class TenantEnabledUserStore : IUserStore<ApplicationUser>, IUserLoginStore<ApplicationUser>,
IUserPasswordStore<ApplicationUser>, IUserSecurityStampStore<ApplicationUser>
{
private bool _disposed;
private AppTenant _tenant;
private readonly ApplicationDbContext _context;
public TenantEnabledUserStore(ApplicationDbContext context, AppTenant tenant)
{
_context = context;
_tenant = tenant;
}
/*... implementation omitted for brevity*/
}
如果用户注册或登录,这可以正常工作。 AppTenant
已正确设置。在SeedData.Initialize(app.ApplicationServices);
方法结束时调用Statup.Configure()
时会出现问题:
public static class SeedData
{
public async static void Initialize(IServiceProvider provider)
{
using (var context = new ApplicationDbContext(
provider.GetRequiredService<DbContextOptions<ApplicationDbContext>>()))
{
var admin = new ApplicationUser
{
AppTenantId = 1,
Email = "foo@bar.com",
UserName = "Administrator",
EmailConfirmed = true
};
if(!context.Users.Any(u => u.Email == admin.Email))
{
var userManager = provider.GetRequiredService<UserManager<ApplicationUser>>();
await userManager.CreateAsync(admin, "Penis123#");
}
context.SaveChanges();
}
}
}
usermanager正在调用自定义用户界面,但现在AppTenant为null。 当代码最终到达
时public Task<ApplicationUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
{
return _context.Users.FirstOrDefaultAsync(u => u.NormalizedUserName == normalizedUserName && u.AppTenantId == _tenant.AppTenantId, cancellationToken);
}
我正面临System.InvalidoperationException
,因为AppTenant在上述用户的构造函数中作为null传递。
我做错了什么?我是以错误的方式播种还是忘记了一些基本的东西?
更新 现在我采用了撬棍方法,避免了用户管理器并使用模拟AppTenant创建了我自己的用户实例:
if (!context.Users.Any(u => u.Email == admin.Email))
{
var userStore = new TenantEnabledUserStore(context, new AppTenant
{
AppTenantId = 1
});
await userStore.SetPasswordHashAsync(admin, new PasswordHasher<ApplicationUser>().HashPassword(admin, "VeryStrongPassword123#"), default(CancellationToken));
await userStore.SetSecurityStampAsync(admin, Guid.NewGuid().ToString("D"), default(CancellationToken));
await userStore.CreateAsync(admin, default(CancellationToken));
}
尽管如此,我仍然对一种更干净的方法感兴趣,并不觉得这种方法很糟糕。
答案 0 :(得分:2)
使用Saaskit时,您需要配置AppTenantResolver
,以确定如何根据提供的TenantContext<T>
设置HttpContext
。然后,它会将检索到的TenantContext<T>
存储在Items
的{{1}}属性中。这是一个Scope级别缓存,因此租户仅在请求期间存储在那里。
当您将HttpContext
注入某个类时,它会尝试从AppTenant
解析它。如果找不到租户,则会注入null。
当您致电HttpContext.Items
时,您不在请求的上下文中,因此SeedData.Initialize(app.ApplicationServices)
中间件尚未运行,并且无法解析AppTenant。
遗憾的是,由于没有完整的代码详细信息,很难确切地说明如何解决您的问题。您需要确保在AppTenantResolver
方法中创建新的Scope
并解析该范围内的SeedData
,以便后续调用IoC将允许其插入。