Thank you so much ismcagdas. That resolves my confusions and gives me better idea.
I think it's better to save tenant users in tenant databases. I'm thinking of a workaround: first use AbpUserAccounts table (via IRepository<UserAccount, long> userAccountRepository) to determine which tenant the user belongs to and then go to to tenant database to perform login, which includes authentication, logging, etc.
Do you see any downside of doing this?
Thank you,
Hi ismcagdas,
I overwrote the SignInManager class and successfully passed _signInManager.SignInOrTwoFactor(loginResult, loginModel.RememberMe);. Thank you very much for the clue.
But after I returned from Login method (return Json(new AjaxResponse { TargetUrl = returnUrl })), the application went through Tenant.cs and passed the connection string of tenant database into the constructor of host database again:
public HostDbContext(string nameOrConnectionString)
: base(nameOrConnectionString) _<-- error happened when tenantDb's connectionstring got passed in._
{
}
How could I prevent using tenantDb's connectionstring in HostDbContext? Or how could I make sure I'm passing tenant connectionstring to tenant constructor?
Thank you @ismcagdas. Seems couple methods in SettingManager need to be overwritten as well..
My project is MVC 5.* and AngularJS. I'm using multi-tenancy structure with one host database and multiple tenant databases.
When we're calling the app.services in angularJS module, I see abp is using IApplicationService to identify AngularJS services. I guess IApplicationService is leveraging abpsession.TenantId information to find the correct database. But even if I set the connectionstring in abptenants table to null and add the tenant connectionstring to web.config, app.services is still able to perform CRUB on correct database. I suspect that IApplicationService is selecting database based on the order of connectionstring in web.config.
Anyway, this part confuses me as lot. Could anyone please explain or provide some material?
Thank you,
Thank you very much Aaron.
After I disable the data filter, I'm able to get the user.
But I'm not sure why when I run the _signInManager.SignInOrTwoFactor with disablefilter
var loginResult= new AbpLoginResult<Tenant, User>(tenant, user, createIdentity);
signInResult = await _signInManager.SignInOrTwoFactor(loginResult, loginModel.RememberMe);
, it still throws me error at hostDb constructor:
public HostDbContext(string nameOrConnectionString)
: base(nameOrConnectionString)
Error Message: System.Data.SqlClient.SqlException: 'There is already an object named 'AbpAuditLogs' in the database.'
The pass-in nameOrConnectionString is the connectionstring of tenant database (saved in abpTenants table on Host Database), which is supposed to be the connectionstring of host database. Tenant database doesn't contain any abp table. Seems the application is trying to create entities again because it doesn't find tables in the tenant database.
How could I disable datafilter and user hostDb for _signInManager?
Thank you,
I use MVC5.* + AngularJS and enabled multitenancy for the project. I separate out host database and tenant databases, keeping all tenant users in the host database and only using tenant database for application services.
So I hope that the workflow of login process would be: find user --> get user.tenantId -> find the connectionstring of the tenant -> route all app.services to the tenant database.
But seems there is an auto-filter on the tenant when I'm logging in. Suppose I'm trying to find a user who belongs to tenant 2 with either UserManager or HostDbContext, the query will automatically filter user by tenant = null as I didn't set UnitOfWorkManager.Current.SetTenantId before I search for the user. If I hard code UnitOfWorkManager.Current.SetTenantId to be 2 before I search, then I'm able to find the user.
var ur = await _userManager.Users.Where(u => u.PhoneNumber == model.MobilePhone).FirstOrDefaultAsync();
or
var hostDbContext = new HostDbContext();
SqlParameter param = new SqlParameter()
{
ParameterName = "@UserID",
SqlDbType = SqlDbType.BigInt,
Direction = ParameterDirection.Output
};
var t = hostDbContext.Database.ExecuteSqlCommand("select top 1 tenantId from abpusers where id = @UserID", param);
Could anyone give some hint on the way to find the user before I set the tenantId in UnitOfWorkManager?
Thank you,
Issue is resolved. Was looking in the wrong place.
Thank you!
I am creating two tables: OrganizationUnitType - containing Id and Name OrganizationUnitDetails - containing more information about OrganizationUnitTable.
In .Core Project, I have create a new class called OrganizationUnitType : [Table("OrganizationUnitType")] public class OrganizationUnitType : Entity {
[Key]
public override int Id { get; set; }
public virtual string Name { get; set; }
public OrganizationUnitType() {
}
public OrganizationUnitType(string _name)
{
Name = _name;
}
}
}
And seeding the Data for the same after creating IDBSet in a new file under Tenants folder like below:
public class OrganizationUnitTypeCreator { private readonly MajesticDbContext _context; public OrganizationUnitTypeCreator() {
}
public OrganizationUnitTypeCreator(MajesticDbContext context)
{
_context = context;
}
public void Create()
{
var organizationUnitType1 = _context.OrganizationUnitTypes.FirstOrDefault(p => p.Name == "Door");
if (organizationUnitType1 == null)
{
_context.OrganizationUnitTypes.Add(
new OrganizationUnitType
{
Name = "ABC"
});
}
_context.SaveChanges();}}
I have also added a MigrationHistory file Up() methodwith the below data to create my second table: - OrganizationUnitDetails
CreateTable(
"dbo.OrganizationUnitDetail",
c => new
{
Id = c.Long(nullable: false, identity: true),
OrganizationUnitId = c.Long(),
OrganizationUnitTypeID = c.Int(),
StreetAddress1 = c.String(nullable: false, maxLength: 256),
StreetAddress2 = c.String(nullable: true, maxLength: 256),
City = c.String(nullable: false, maxLength: 256),
State = c.String(nullable: false, maxLength: 256),
PostalCode = c.String(nullable: false, maxLength: 256)
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.AbpOrganizationUnits", t => t.OrganizationUnitId, cascadeDelete: true)
.ForeignKey("dbo.OrganizationUnitType", t => t.OrganizationUnitTypeID, cascadeDelete: true);
After doing the above things - when I try to run the update command I am getting this error:
Column 'dbo.OrganizationUnitType.Id' is not the same data type as referencing column 'PermissionOrganizationUnitType.OrganizationUnitTypeId' in foreign key 'FK_dbo.PermissionOrganizationUnitType_dbo.OrganizationUnitType_OrganizationUnitTypeId'. Could not create constraint. See previous errors.
I don't quite understand because the data type I have specified in class for OrganizationUnitType is same as the foreign key specified in migration history file.
Can someone please point me in the right direction?
Yes, I had tried that. But still shows up as POST method in Swagger