Base solution for your next web application
Open Closed

Error While Creating Tenant in Different Database #1556


User avatar
0
maharatha created

Hi -

We are trying to create a new tenant in a new database and we get the below error :

Castle.MicroKernel.ComponentActivator.ComponentActivatorException: ComponentActivator: could not instantiate XX.XXXXX.EntityFramework.XXXXXXXDbContext ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Data.SqlClient.SqlException: CREATE DATABASE statement not allowed within multi-statement transaction. at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async, Int32 timeout, Boolean asyncWrite) at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite) at System.Data.SqlClient.SqlCommand.ExecuteNonQuery() at System.Data.Entity.Infrastructure.Interception.InternalDispatcher1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget target, Func3 operation, TInterceptionContext interceptionContext, Action3 executing, Action3 executed) at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.NonQuery(DbCommand command, DbCommandInterceptionContext interceptionContext) at System.Data.Entity.SqlServer.SqlProviderServices.<>c__DisplayClass1a.b__19(DbConnection conn) at System.Data.Entity.SqlServer.SqlProviderServices.<>c__DisplayClass33.b__32() at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.<>c__DisplayClass1.b__0() at System.Data.Entity.SqlServer.Def... CLOSE

This error doesn't appear everytime but have started coming more often.

Can you let me know what could be the issue ?

Thanks, Partha


4 Answer(s)
  • User Avatar
    0
    maharatha created

    Any update on this ? I am really having a hard time figuring out the issue

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    We didn't have this error before ?

    Does it happen with same connectionString ? First you get error then after a few trials you manage to create tenant and database ?

    And is your Sql Server on a local server ?

  • User Avatar
    0
    maharatha created

    Below is the code which we wrote for creating the Tenant. We copied from the existing code and made some modification. We are adding an Organization Id to the tenant.

    Also it's withing the same database and the database is not local.

    public async Task<int> CreateWithAdminUserAsync(string tenancyName, string name, string adminPassword, string adminEmailAddress, bool isActive, int? editionId, bool shouldChangePasswordOnNextLogin,
                bool sendActivationEmail, long? organizationId, int? sourcetenantId, List<string> entityList)
            {
                int newTenantId;
                long newAdminId;
    
                using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.RequiresNew))
                {
    
                    var connectionstringUnit = await (from org in _organizationRepository.GetAll()
                                                      join constr in _connectionStringRepository.GetAll() on org.ConnectionStringId equals
                                                          constr.Id
                                                      where org.Id == organizationId
                                                      select constr).FirstOrDefaultAsync();
    
                    //Create tenant
                    var tenant = new Tenant(tenancyName, name)
                    {
                        IsActive = isActive,
                        EditionId = editionId,
                        ConnectionString = ReferenceEquals(connectionstringUnit, null) ? null : connectionstringUnit.ConnectionString,
                        OrganizationUnitId = organizationId
                    };
    
                    CheckErrors(await CreateAsync(tenant));
                    await _unitOfWorkManager.Current.SaveChangesAsync(); //To get new tenant's id.
    
                    //Create tenant database
                    _abpZeroDbMigrator.CreateOrMigrateForTenant(tenant);
    
                    //We are working entities of new tenant, so changing tenant filter
                    using (_unitOfWorkManager.Current.SetTenantId(tenant.Id))
                    {
                        //Create static roles for new tenant
                        CheckErrors(await _roleManager.CreateStaticRoles(tenant.Id));
                        await _unitOfWorkManager.Current.SaveChangesAsync(); //To get static role ids
    
                        //grant all permissions to admin role
                        var adminRole = _roleManager.Roles.Single(r => r.Name == StaticRoleNames.Tenants.Admin);
                        await _roleManager.GrantAllPermissionsAsync(adminRole);
    
                        //User role should be default
                        var userRole = _roleManager.Roles.Single(r => r.Name == StaticRoleNames.Tenants.User);
                        userRole.IsDefault = true;
                        CheckErrors(await _roleManager.UpdateAsync(userRole));
    
                        //Create admin user for the tenant
                        if (adminPassword.IsNullOrEmpty())
                        {
                            adminPassword = User.CreateRandomPassword();
                        }
    
                        var adminUser = User.CreateTenantAdminUser(tenant.Id, adminEmailAddress, adminPassword);
                        adminUser.ShouldChangePasswordOnNextLogin = shouldChangePasswordOnNextLogin;
                        adminUser.IsActive = isActive;
    
                        CheckErrors(await _userManager.CreateAsync(adminUser));
                        await _unitOfWorkManager.Current.SaveChangesAsync(); //To get admin user's id
    
                        //Assign admin user to admin role!
                        CheckErrors(await _userManager.AddToRoleAsync(adminUser.Id, adminRole.Name));
    
                        //Notifications
                        await _appNotifier.WelcomeToTheApplicationAsync(adminUser);
    
                        //Send activation email
                        if (sendActivationEmail)
                        {
                            adminUser.SetNewEmailConfirmationCode();
                            await _userEmailer.SendEmailActivationLinkAsync(adminUser, adminPassword);
                        }
    
                        await _unitOfWorkManager.Current.SaveChangesAsync();
    
                        await _demoDataBuilder.BuildForAsync(tenant);
    
                        newTenantId = tenant.Id;
                        newAdminId = adminUser.Id;
    
                    }
                    if (sourcetenantId.HasValue)
                        await CloneTenantData(newTenantId, sourcetenantId, entityList);
                    await uow.CompleteAsync();
                }
    
                //Used a second UOW since UOW above sets some permissions and _notificationSubscriptionManager.SubscribeToAllAvailableNotificationsAsync needs these permissions to be saved.
                using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.RequiresNew))
                {
                    using (_unitOfWorkManager.Current.SetTenantId(newTenantId))
                    {
                        await _notificationSubscriptionManager.SubscribeToAllAvailableNotificationsAsync(new UserIdentifier(newTenantId, newAdminId));
                        await _unitOfWorkManager.Current.SaveChangesAsync();
                        await uow.CompleteAsync();
                    }
                }
    
                return newTenantId;
            }
    

    Do you find any issue with this code ?

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Can you try to move added codes in a seperate unitOfWork ?

    Move these lines before first unitOfWork and put it inside another unit of work

    var connectionstringUnit = await (from org in _organizationRepository.GetAll()
                                  join constr in _connectionStringRepository.GetAll() on org.ConnectionStringId equals
                                      constr.Id
                                  where org.Id == organizationId
                                  select constr).FirstOrDefaultAsync();
    

    and put CloneTenantData just before these lines and put it in a new unitOfWork

    //Used a second UOW since UOW above sets some permissions and _notificationSubscriptionManager.SubscribeToAllAvailableNotificationsAsync needs these permissions to be saved.
    using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.RequiresNew))
    

    Can you also share CloneTenantData code if this does not work for you ?

    Thanks.