Open Closed

Data Seeder not working as expected for Tenant only Entity in Split Tenant DB Configuration #8896


User avatar
0
jarrad78 created
  • ABP Framework version: v9.0.4

  • UI Type: MVC

  • Database System: EF Core (MySQL)

  • Tiered (for MVC) or Auth Server Separated (for Angular): no

  • Exception message and full stack trace:

  • GITHUB Project With Error: https://github.com/jarrad1978/TestSeedProject.git

I generated sample crud page using abp studio. I then changed configuration so that Books entity is moved to tenant db instead of host db.

What I am trying to achieve

Create Books in Tenant only DB Context so that Books table does not exist in Host DB but is only created in Tenant DB and then seed initial book data into the tenant only table

What error is happening

When I attempt to run migrations for a newly created tenant the migration process fails when data seeding commences. The data seeder is attempting to check the Book repository for records in the host db but it doesn't exist there. It is only present in Tenant DB Context.

Steps taken so far

1.) Removed Books Entity configuration from TestSeedProjectDbContextBase (DbSet and builder.Entity config)
2.) Added Books Entity configuration to TestSeedProjectTenantDbContext (DbSet and builder.Entity config)
3.) Generated Host and Tenant DB Migrations
4.) Modified BookStoreDataSeederContributor and injected ICurrentTenant so that I can skip seeding for HOST.
4.) Executed Data Seeder (Worked as Expected and skipped host DB)
5.) Start web application,
6.) login as host admin,
7.) create a new tenant,
8.) set tenant DB connection string in SaaS module.
9.) Rerun Migrations for new Tenant and Data Seeding for book fails with an error indicating that it is attempting to get the Books record
count from the host DB.
(Exception is included at the bottom of support ticket)

Steps to replicate

1.) Download github project (link above)
2.) Update references to AbpLicenseCode with a valid key
3.) run compose.yml to start backend services
4.) start web project, login as host admin
5.) navigate to SaaS->Tenants and attempt to apply database migrations for tenant.

The only thing I changed was to try to configure the sample crud Books entity as a Tenant only table, the DB migrations are applied successfully and work as expected.
(AppBooks created in tenant DB and not host DB - expected)

The Data Seeder does not function as expected,
I would expect that the Data Seeder would not try to resolve the Book Repository via the HOST DB since Book is configured in TenantDb Context only.

Exception

This is the exception thrown, it's clear that it's attempting to seed the data in the HOST DB instead of the TenantDB.

current tenant: 3a186f02-97f1-9502-996f-12b851942753

[11:11:26 DBG] Executing HealthCheck collector HostedService.
[11:11:26 INF] Start processing HTTP request GET https://localhost:44365/health-status
[11:11:26 INF] Sending HTTP request GET https://localhost:44365/health-status
[11:11:26 INF] Request starting HTTP/1.1 GET https://localhost:44365/health-status - null null
[11:11:26 DBG] The event OpenIddict.Validation.OpenIddictValidationEvents+ProcessRequestContext was successfully processed by OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlers+ResolveRequestUri.
[11:11:26 DBG] The event OpenIddict.Server.OpenIddictServerEvents+ProcessRequestContext was successfully processed by OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlers+ResolveRequestUri.
[11:11:26 DBG] The event OpenIddict.Server.OpenIddictServerEvents+ProcessRequestContext was successfully processed by OpenIddict.Server.OpenIddictServerHandlers+InferEndpointType.
[11:11:26 DBG] The event OpenIddict.Server.OpenIddictServerEvents+ProcessRequestContext was successfully processed by Volo.Abp.Account.Web.Pages.Account.OpenIddictImpersonateInferEndpointType.
[11:11:26 DBG] The event OpenIddict.Server.OpenIddictServerEvents+ProcessRequestContext was successfully processed by OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlers+ValidateTransportSecurityRequirement.
[11:11:26 DBG] The event OpenIddict.Server.OpenIddictServerEvents+ProcessRequestContext was successfully processed by OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlers+ValidateHostHeader.
[11:11:26 DBG] The event OpenIddict.Validation.OpenIddictValidationEvents+ProcessAuthenticationContext was successfully processed by OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlers+ValidateHostHeader.
[11:11:26 DBG] The event OpenIddict.Validation.OpenIddictValidationEvents+ProcessAuthenticationContext was successfully processed by OpenIddict.Validation.OpenIddictValidationHandlers+EvaluateValidatedTokens.
[11:11:26 DBG] The event OpenIddict.Validation.OpenIddictValidationEvents+ProcessAuthenticationContext was successfully processed by OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlers+ExtractAccessTokenFromAuthorizationHeader.
[11:11:26 DBG] The event OpenIddict.Validation.OpenIddictValidationEvents+ProcessAuthenticationContext was successfully processed by OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlers+ExtractAccessTokenFromBodyForm.
[11:11:26 DBG] The event OpenIddict.Validation.OpenIddictValidationEvents+ProcessAuthenticationContext was successfully processed by OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlers+ExtractAccessTokenFromQueryString.
[11:11:26 DBG] The event OpenIddict.Validation.OpenIddictValidationEvents+ProcessAuthenticationContext was successfully processed by OpenIddict.Validation.OpenIddictValidationHandlers+ValidateRequiredTokens.
[11:11:26 DBG] The event OpenIddict.Validation.OpenIddictValidationEvents+ProcessAuthenticationContext was marked as rejected by OpenIddict.Validation.OpenIddictValidationHandlers+ValidateRequiredTokens.
[11:11:27 ERR] Failed executing DbCommand (219ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT COUNT(*)
FROM AppBooks AS a
[11:11:27 ERR] An exception occurred while iterating over the results of a query for context type 'TestSeedProject.EntityFrameworkCore.TestSeedProjectTenantDbContext'.
MySqlConnector.MySqlException (0x80004005): Table 'testseedproject.appbooks' doesn't exist
at MySqlConnector.Core.ServerSession.ReceiveReplyAsync(IOBehavior ioBehavior, CancellationToken cancellationToken) in //src/MySqlConnector/Core/ServerSession.cs:line 1081
at MySqlConnector.Core.ResultSet.ReadResultSetHeaderAsync(IOBehavior ioBehavior) in /
/src/MySqlConnector/Core/ResultSet.cs:line 37
at MySqlConnector.MySqlDataReader.ActivateResultSet(CancellationToken cancellationToken) in //src/MySqlConnector/MySqlDataReader.cs:line 131
at MySqlConnector.MySqlDataReader.InitAsync(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, IDictionary2 cachedProcedures, IMySqlCommand command, CommandBehavior behavior, Activity activity, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlDataReader.cs:line 487 at MySqlConnector.Core.CommandExecutor.ExecuteReaderAsync(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, CommandBehavior behavior, Activity activity, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/CommandExecutor.cs:line 56 at MySqlConnector.MySqlCommand.ExecuteReaderAsync(CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlCommand.cs:line 357 at MySqlConnector.MySqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlCommand.cs:line 350 at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)
at Pomelo.EntityFrameworkCore.MySql.Storage.Internal.MySqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func4 operation, Func4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable1.AsyncEnumerator.MoveNextAsync() MySqlConnector.MySqlException (0x80004005): Table 'testseedproject.appbooks' doesn't exist at MySqlConnector.Core.ServerSession.ReceiveReplyAsync(IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/ServerSession.cs:line 1081 at MySqlConnector.Core.ResultSet.ReadResultSetHeaderAsync(IOBehavior ioBehavior) in /_/src/MySqlConnector/Core/ResultSet.cs:line 37 at MySqlConnector.MySqlDataReader.ActivateResultSet(CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlDataReader.cs:line 131 at MySqlConnector.MySqlDataReader.InitAsync(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, IDictionary2 cachedProcedures, IMySqlCommand command, CommandBehavior behavior, Activity activity, IOBehavior ioBehavior, CancellationToken cancellationToken) in /
/src/MySqlConnector/MySqlDataReader.cs:line 487
at MySqlConnector.Core.CommandExecutor.ExecuteReaderAsync(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, CommandBehavior behavior, Activity activity, IOBehavior ioBehavior, CancellationToken cancellationToken) in //src/MySqlConnector/Core/CommandExecutor.cs:line 56
at MySqlConnector.MySqlCommand.ExecuteReaderAsync(CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken) in /
/src/MySqlConnector/MySqlCommand.cs:line 357
at MySqlConnector.MySqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) in //src/MySqlConnector/MySqlCommand.cs:line 350
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken) at Pomelo.EntityFrameworkCore.MySql.Storage.Internal.MySqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func4 operation, Func4 verifySucceeded, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable1.AsyncEnumerator.MoveNextAsync()
[11:11:27 ERR] Table 'testseedproject.appbooks' doesn't exist
MySqlConnector.MySqlException (0x80004005): Table 'testseedproject.appbooks' doesn't exist
at MySqlConnector.Core.ServerSession.ReceiveReplyAsync(IOBehavior ioBehavior, CancellationToken cancellationToken) in /
/src/MySqlConnector/Core/ServerSession.cs:line 1081
at MySqlConnector.Core.ResultSet.ReadResultSetHeaderAsync(IOBehavior ioBehavior) in //src/MySqlConnector/Core/ResultSet.cs:line 37
at MySqlConnector.MySqlDataReader.ActivateResultSet(CancellationToken cancellationToken) in /
/src/MySqlConnector/MySqlDataReader.cs:line 131
at MySqlConnector.MySqlDataReader.InitAsync(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, IDictionary2 cachedProcedures, IMySqlCommand command, CommandBehavior behavior, Activity activity, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlDataReader.cs:line 487 at MySqlConnector.Core.CommandExecutor.ExecuteReaderAsync(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, CommandBehavior behavior, Activity activity, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/CommandExecutor.cs:line 56 at MySqlConnector.MySqlCommand.ExecuteReaderAsync(CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlCommand.cs:line 357 at MySqlConnector.MySqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlCommand.cs:line 350 at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)
at Pomelo.EntityFrameworkCore.MySql.Storage.Internal.MySqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func4 operation, Func4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable1.AsyncEnumerator.MoveNextAsync() at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleAsync[TSource](IAsyncEnumerable1 asyncEnumerable, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleAsync[TSource](IAsyncEnumerable1 asyncEnumerable, CancellationToken cancellationToken) at Volo.Abp.Domain.Repositories.EntityFrameworkCore.EfCoreRepository2.GetCountAsync(CancellationToken cancellationToken)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at TestSeedProject.TestSeedProjectDataSeederContributor.SeedAsync(DataSeedContext context) in D:\Workspace\DevTest\TestSeedProject\src\TestSeedProject.Domain\BookStoreDataSeederContributor.cs:line 33 at Volo.Abp.Data.DataSeeder.SeedAsync(DataSeedContext context) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous(IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapter.ProceedAsync() at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at TestSeedProject.Data.TestSeedProjectTenantDatabaseMigrationHandler.MigrateAndSeedForTenantAsync(Guid tenantId, String adminEmail, String adminPassword) in D:\Workspace\DevTest\TestSeedProject\src\TestSeedProject.Domain\Data\TestSeedProjectTenantDatabaseMigrationHandler.cs:line 115

[11:11:27 INF] Request finished HTTP/2 POST https://localhost:44365/api/saas/tenants/3a186f02-97f1-9502-996f-12b851942753/apply-database-migrations - 204 null null 25788.7567ms


6 Answer(s)
  • User Avatar
    0
    EngincanV created
    Support Team .NET Developer

    Hi, in your data seeder, you should use ICurrentTenant.Change method to change the current tenant, otherwise, it will see the current tenant as host. Please refer to https://abp.io/docs/latest/framework/architecture/multi-tenancy#change-the-current-tenant, and if you can't work it out, then please share the data seed contributor so I can better assist you.

    Best regards.

  • User Avatar
    0
    jarrad78 created
    GITHUB Project With Error: https://github.com/jarrad1978/TestSeedProject.git (data seeder in Domain Project)

    I'm aware of the ICurrentTenant.Change method and how it's supposed to switch DB Context.

    Despite ICurrentTenant.Change being called twice (first in TestSeedProjectTenantDatabaseMigrationHandler.cs and then again in BookStoreDataSeederContributor.cs), the seeder seems to revert to the host database context for unknown reasons.

    Sequence of Events:

    TestSeedProjectTenantDatabaseMigrationHandler.cs: ICurrentTenant.Change is called, seemingly setting the correct tenant context prior to the call to _dataSeeder.SeedAsync.

    BookStoreDataSeederContributor.cs: The tenant context appears correct initially (due to already being called in TestSeedProjectTenantDatabaseMigrationHandler.cs) and ICurrentTenant.Change is called again in the BookStoreDataSeederContributor.cs class (as a precaution and per your suggestion). When the seeder attempts to access the tenant-only table, it queries the host schema instead.

    Problem:

    The tenant context is not being maintained consistently throughout the seeding process, even though ICurrentTenant.Change is called multiple times.

    Evidence:

    Video 1: Shows the AppBooks table correctly created in the tenant schema. - https://youtube.com/shorts/HOEFQNgWc-I
    Video 2: Demonstrates the seeder querying the host schema after ICurrentTenant.Change, even though the correct tenant context seems to be already selected. - https://youtu.be/yAGVvT6NvQc

    image.png
    image.png

    Environment:

    ABP Framework with split tenant schema

    Any guidance on how to ensure the data seeder correctly uses the tenant database context would be greatly appreciated.

    Additional Notes:
    It seems that the TestSeedProjectTenantDatabaseMigrationHandler.cs already switches the tenant context using ICurrentTenant.Change within a unit of work before calling the _dataSeeder.SeedAsync method. This is why the Books data seeder shows the correct tenant context initially. The issue is isolated to the data seeding process. Database migrations and schema creation work as expected.

    you can pull the lastest from the github project and see the error in action.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Please make your GitHub repos PRIVATE.

    I will check your project.

    Thanks.

  • User Avatar
    1
    maliming created
    Support Team Fullstack Developer

    You have to add IMultiTenant interface to your book entity.

    image.png

  • User Avatar
    0
    jarrad78 created

    Thank you, I was under the mistaken impression that IMultiTenant was not mandatory for a Split Tenant Schema scenario. This resolved my issue.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Great : )

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
Do you need assistance from an ABP expert?
Schedule a Meeting
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v9.2.0-preview. Updated on March 13, 2025, 04:08