From 41feb481e4f2c47821cc9b4fd1138dc7401c082d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 11 May 2021 19:42:05 +0300 Subject: [PATCH] Resolved #7661: Document IgnoreMultiTenancy usage on dbcontexts --- docs/en/Entity-Framework-Core-Migrations.md | 61 +++++++++++++++++++++ docs/en/Entity-Framework-Core.md | 20 ++++++- docs/en/MongoDB.md | 18 ++++++ 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/docs/en/Entity-Framework-Core-Migrations.md b/docs/en/Entity-Framework-Core-Migrations.md index be5968a9af..4d42d8bb97 100644 --- a/docs/en/Entity-Framework-Core-Migrations.md +++ b/docs/en/Entity-Framework-Core-Migrations.md @@ -881,6 +881,67 @@ We had a reference to the `Acme.BookStore.EntityFrameworkCore.DbMigrationsForSec You can run the `.DbMigrator` application to migrate & seed the databases. To test, you can delete both databases and run the `.DbMigrator` application again to see if it creates both of the databases. +## Separating Host & Tenant Database Schemas + +In a multi-tenant solution, you may want to separate your database schemas, so host-related tables don't locate in the tenant databases when tenants have separate databases. + +Some pre-built ABP modules are related only with the host side, like the [Tenant Management](Modules/Tenant-Management.md) module. So, in the tenant `DbContext` class you don't call `modelBuilder.ConfigureTenantManagement()` and that's all. + +However, some modules, like the [Identity](Modules/Identity.md) module, is both used in host and tenant sides. It stores tenant users in the tenant database and host users in the host database. However, it stores some entities, like `IdentityClaimType`, only in the host side. In this case, you don't want to add these tables in the tenant database, even if they are not used and will always be empty. + +ABP provides a simple way to set the multi-tenancy side for a `DbContext`, so the modules can check it and decide to map tables to the database, or not. + +````csharp +public class MyTenantDbContext : AbpDbContext +{ + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.SetMultiTenancySide(MultiTenancySides.Tenant); + + base.OnModelCreating(modelBuilder); + modelBuilder.ConfigureIdentity(); + modelBuilder.ConfigureFeatureManagement(); + modelBuilder.ConfigureAuditLogging(); + } +} +```` + +The first line in the `OnModelCreating` sets multi-tenancy side to `Tenant`. For this example, Feature management tables are not created (because all the tables are host-specific), so calling `modelBuilder.ConfigureFeatureManagement()` has no effect. Also, `ConfigureIdentity()` call respects to the multi-tenancy side and doesn't create host-specific tables for this database. + +`SetMultiTenancySide` can get the following values: + +* `MultiTenancySides.Both` (**default value**): This `DbContext` (and the related database) is shared by host and tenant. +* `MultiTenancySides.Host`: This `DbContext` (and the related database) is used only by the host side. +* `MultiTenancySides.Tenant`: This `DbContext` (and the related database) is only for tenants. + +If you create a re-usable application module or want to check that value in your application code, you can use `modelBuilder.GetMultiTenancySide()` to check the current side. + +````csharp +var side = modelBuilder.GetMultiTenancySide(); +if (!side.HasFlag(MultiTenancySides.Host)) +{ + ... +} +```` + +Or practically you can use one of the shortcut extension methods: + +````csharp +if (modelBuilder.IsTenantOnlyDatabase()) +{ + ... +} +```` + +There are four methods to check the current side: + +* `IsHostDatabase()`: Returns `true` if you should create host-related tables. It is equivalent of checking `modelBuilder.GetMultiTenancySide().HasFlag(MultiTenancySides.Host)`. +* `IsHostOnlyDatabase()`: Returns `true` if you should only create host-related tables, but should not create tenant-related tables. It is equivalent of checking `modelBuilder.GetMultiTenancySide() == MultiTenancySides.Host`. +* `IsTenantDatabase()`: Returns `true` if you should create tenant-related tables. It is equivalent of checking `modelBuilder.GetMultiTenancySide().HasFlag(MultiTenancySides.Tenant)`. +* `IsTenantOnlyDatabase()`: Returns `true` if you should only create tenant-related tables, but should not create host-related tables. It is equivalent of checking `modelBuilder.GetMultiTenancySide() == MultiTenancySides.Tenant`. + +All pre-built ABP [modules](Modules/Index.md) checks this value in their `modelBuilder.ConfigureXXX()` methods. + ## Conclusion This document explains how to split your databases and manage your database migrations of your solution for Entity Framework Core. In brief, you need to have a separate migration project per different databases. diff --git a/docs/en/Entity-Framework-Core.md b/docs/en/Entity-Framework-Core.md index a2c0e79996..241a08790f 100644 --- a/docs/en/Entity-Framework-Core.md +++ b/docs/en/Entity-Framework-Core.md @@ -626,6 +626,24 @@ See the "*ConfigureByConvention Method*" section above for more information. ## Advanced Topics +### Controlling the Multi-Tenancy + +If your solution is [multi-tenant](Multi-Tenancy.md), tenants may have **separate databases**, you have **multiple** `DbContext` classes in your solution and some of your `DbContext` classes should be usable **only from the host side**, it is suggested to add `[IgnoreMultiTenancy]` attribute on your `DbContext` class. In this case, ABP guarantees that the related `DbContext` always uses the host [connection string](Connection-Strings.md), even if you are in a tenant context. + +**Example:** + +````csharp +[IgnoreMultiTenancy] +public class MyDbContext : AbpDbContext +{ + ... +} +```` + +Do not use the `[IgnoreMultiTenancy]` attribute if any one of your entities in your `DbContext` can be persisted in a tenant database. + +> When you use repositories, ABP already uses the host database for the entities don't implement the `IMultiTenant` interface. So, most of time you don't need to `[IgnoreMultiTenancy]` attribute if you are using the repositories to work with the database. + ### Set Default Repository Classes Default generic repositories are implemented by `EfCoreRepository` class by default. You can create your own implementation and use it for all the default repository implementations. @@ -782,4 +800,4 @@ public class MyCustomEfCoreBulkOperationProvider ## See Also * [Entities](Entities.md) -* [Repositories](Repositories.md) +* [Repositories](Repositories.md) \ No newline at end of file diff --git a/docs/en/MongoDB.md b/docs/en/MongoDB.md index a12d216b3d..5bcdfe7730 100644 --- a/docs/en/MongoDB.md +++ b/docs/en/MongoDB.md @@ -292,6 +292,24 @@ Configure(options => ### Advanced Topics +### Controlling the Multi-Tenancy + +If your solution is [multi-tenant](Multi-Tenancy.md), tenants may have **separate databases**, you have **multiple** `DbContext` classes in your solution and some of your `DbContext` classes should be usable **only from the host side**, it is suggested to add `[IgnoreMultiTenancy]` attribute on your `DbContext` class. In this case, ABP guarantees that the related `DbContext` always uses the host [connection string](Connection-Strings.md), even if you are in a tenant context. + +**Example:** + +````csharp +[IgnoreMultiTenancy] +public class MyDbContext : AbpMongoDbContext +{ + ... +} +```` + +Do not use the `[IgnoreMultiTenancy]` attribute if any one of your entities in your `DbContext` can be persisted in a tenant database. + +> When you use repositories, ABP already uses the host database for the entities don't implement the `IMultiTenant` interface. So, most of time you don't need to `[IgnoreMultiTenancy]` attribute if you are using the repositories to work with the database. + #### Set Default Repository Classes Default generic repositories are implemented by `MongoDbRepository` class by default. You can create your own implementation and use it for default repository implementation.