Base solution for your next web application

Activities of "JeffMH"

I am wondering if anyone has any experience in converting the framework to work with a database per tenant. With Azure Elastic DB coming out, this is a great way to architect a multi-tenant solution. Having worked with a very large Multiple-Tenant, Single Database before, I just can't see using this ever again. Very limited on what you can do to scale out. Not a fan of sharding. Of course, this is my preference and doesn't have to be everyone's, so choices are good!

<a class="postlink" href="https://channel9.msdn.com/Events/Build/2016/P522">https://channel9.msdn.com/Events/Build/2016/P522</a>

So, I am setting off on the journey to modify this to work with multiple databases. I am hoping some thought has been put into this by others and hoping to have a discussion about what it takes. I hope I can give back to the community with this. So, I don't mind doing some work here, but hoping I can get a little assistance with this to get started.

Here are my thoughts and I am curious to see if anyone else has thought about this or might make sure that my logic is correct:

Design

  • Host DB and TenantDB are the same root data model from a frameworks perspective. Can I leave out the new tables I create? Do I care if we can't? If I can't, maybe I need a different context and setup for the domain specific tables(Product, Customer, etc). Keep the migrations seperate.
  • Create the same tenant in both the Host and Tenant. Use Same TenantId in both to tie the two together. This allows me to do some fancy reporting at some point by tying the ID's together nicely. Maybe I have some ETL that extracts certain information about each tenant into a data warehouse. I would leave each database in multi-tenant mode. This also may allow us to do a hybrid scenario. Maybe a premium addition comes with it's own Database or you could just spread out your tenants to seperate DB's (forced sharding).
  • Store URL, DB Server and Database as attributes on a tenant. Tenant Settings won't work here since I need to know what store to get the settings from. This is determined based on URL.
  • When a login request comes in, check Host DB for the Tenant by URL and gather DB information for all connections. For Host URL, this would be the master database. For Tenant URL's, this would be the individual tenant database.
  • All remaining request use the Tenant settings to know which DB to hit.

Changes that I see:

  • Will need to set the Tenant connection dynamically. Need access to the settings from within the DbContext to set the connection dynamically.
  • In the Context, add some code to dynamically set the connection based on the current Tenant. Use Host by default if one isn't set.
  • What settings need to be in the Tenant, what settings need to be in the Host. Email, user settings, etc. May need to add a HostSettings. How does this impact SettingManager.
  • Would the Linked Accounts feature work? To me this is not a deal breaker. It's ok if this feature only works within a single tenant but would be nice if it worked accross tenants.

Any thoughts would be very helpful. I am sure you all have thought about this and any pointers would be helpful.

Is it documented somewhere what I can remove from the web application if I want to do the SPA vs MPA? I would like to get rid of the MPA version to limit developer confustion. We removed Area, but I wanted to double check and make sure if there was anything else or I already got it wrong lol.

Thanks

I have a question. I am finally getting around to migrating over to the new release(which is awesome by the way!). I guess a quick recap of what I am doing:

I am splitting the context. I have a HostDbContext (inherits from AbpZeroHostDbContext) and an TenantDbContext (inherits from AbpZeroTenantDbContext). I am making the Host database strictly Hosts, if I am going to have a Shared database, it will not be in the Host DB. This makes Migrations much simpler IMO.

So, as I am making my changes, I come accross the AbpZeroDbMigrator. I notice this object that it inherits from will run migrations against both the Host and the Tenant databases but the class is only accepting one Context / Migration Configuration:

using Abp.Dependency;
using Abp.Domain.Uow;
using Abp.MultiTenancy;
using Abp.Zero.EntityFramework;

namespace MyPortal.Framework.EntityFramework
{
    public class AbpZeroDbMigrator : AbpZeroDbMigrator<TenantDbContext, Migrations_Tenant.Configuration>
    {

I changed this code to use just the TenantDbContext and the Tenant Migrations but this will only make the Create Tenant part of the system work. The Migrator console application needs to execute Migrator code for both the Host and the Tenant.

I am not sure what was intended for me to do here. Do I create a AbpTenantDbMigrator and AbpHostDbMigrator and modify the console application accordingly? I could implement another MultiTenantMigratorExecuter that would take in both Migrators....

Anyway, I am just looking for guidance. I sort of worked my way through what I thought you were intending me to do but there is not alot of guidance on using Multiple contexts, how to use the current migrations with both contexts, etc. I think you all need to come up with an example of using seperate DB's so we can see what you intended for us to do when using multiple contexts.

I will try and create a document on what I did. I would really like to know if I am doing everything "as intended". Once I get this part worked out, hopefully I can get that done.

Thanks!

Question

In a multi-tenant environment, it would be nice to have a setting at the tenant level that was the "host" url.

host.somecompany.com.

As you parse the URL for the tenant name, if it was that setting value, then it would use the Host as the tenant. Right now, I have this out on Azure so I can go to the root website URL to get into the Host, but I would like to give my admin users a proper URL to get to the host.

If there is another way to do this let me know.

Question

I am on v0.9.6 of the framework:

I am running this code and when I run it, the IMustHaveTenant filter is not getting reset properly on exit of the Using.

using (UnitOfWorkManager.Current.SetTenantId(null))
{
    result = _tenantRepository.Get(tenantId.Value);
}

UnitOfWorkManager.Current.SetTenantId(tenantId.Value);

Before I execute the using, there is one FilterParameters in the MustHaveTenant data filter with tenantId = 7. Upon calling SetTenantId(Null), the filter parameter value tenantId turns to 0 as expected. Immediately after the using block, the MustHaveTenant FilterParameters is removed, there is no parameters in the data filter. I expect this to turn back to the way it was before I called SetTenantId(null). This is causing my next get from a MustHaveTenant table to fail because it passes Zero as the parameter value to Sql.

Right now, I am working around the issue by calling the SetTenantId(tenantId.value). This sets the parameters back to the way it was. But, I shouldn't need to call that. This is actually causing me several issues throughout the application.

I looked through the current code on GitHub and I don't see anything that jumps out at me as though it has changed in the latest version of the BaseUnitOfWork.

Here is a video of what is happening:

[https://1drv.ms/v/s!Am91meCOztkro7p10v62y081S_XS_w])

Thanks!

I see where we can publish a notification by Entity (using EntityIdentifier), but I don't see a built in way to query by that. Like the list of Notifications built into the app, I need to create a list to see the all notifications for a particular Entity.

Is there a way to do this that I am just missing? It's probably wishful thinking but I thought I would ask.

Is it documented anywhere what needs to be done in order to load balance a system based off aspnetzero?

Just things that I know I need to check on are:

  1. Cache items
  2. Background jobs
  3. Notifications

Just trying to see if it's documented somewhere all the things that need to be done within aspnetzero to run multiple instances of a web server serving one app. I don't want to forget anything and something end up not working right.

Thanks

We are struggling coming up with the right place to add code to seed tenant data. There are two scenarios when this needs to occur:

  1. When we create a new tenant
  2. On Database Migration.

We attempted to use the AbpZeroDbMigrator and pass in a seedAction to the CreateOrMigrateTenant function. The method that excepts this parameter is not part of IAbpZeroDbMigrator so we we couldn't call that within the TenantManager. We also tried overriding this method from within the AbpZeroDbMigrator in the EfCore project, and that didn't work because of a UnitOfWork / transaction locking issue.

Anyway, am I missing something easy here? We have default tenant data that has to be put in when we create and do migrations but I am am just not seeing a built in way to do this. I see the one for Hosts, but not tenants. Any help would be appreciated.

Using the latest version downloaded a couple weeks ago.

I have an email going out that will "approve" a request by a user. Because I know I will get that look like "what, why you doing this", let me quickly explain.

Think of this as a fancy bug tracker (it's more complicate than that)

  1. User requests that someone do something for them (Add me to this item please).
  2. This sends an email to the person in charge that says, Mr. Nacho requests that you give them access to Item 1234. Click the link to approve request.
  3. The user will be on their phone 9 times out of 10, I don't want them to login. The link has a special hashed code, I check the code, and see "who are you and what action are you performing".
  4. I validate that the URL is valid, and it hasn't been clicked before, and execute the request.

So, my problem is that AbpSession is null because the request that is running is anonymous. But I need to impersonate the user that is making the request. As the code gets into the repository layer, things start to check for current user for things like audit fields and whatnot and starts to fail. I also have some custom code that saves history of records and it needs a current user in order to run correctly.

Can I accomplish this? I copied code out of the Impersonate functions to sign in as a user, but when I execute _authenticationManager.SignOutAllAndSignIn(), AbpSession does not change. I assume the Claim does not populate in Identity until you call back into the server, which means AbpSession does not ever populate.

Help! (Using the asp.net / angular 1.x version FYI).

I need to get some advice on how to set default permissions on static roles not during tenant creation but during database emigrations. . I have several new permissions that are being created for a new feature. The associated permissions need to be turned on for several roles and I am wondering if there is a clean way to handle this.

For one, the permission isn't actually added into the system until Startup right? So, when I run the migrations through the migrator application, the permissions don't actually exists in the database yet for me to assign to a role.

Is there some guidance on this?

Showing 1 to 10 of 12 entries