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 ?
Any update on this ? I am really having a hard time figuring out the issue
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(TaskCompletionSource
1 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, Func
3 operation, TInterceptionContext interceptionContext, Action3 executing, Action
3 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
I was trying to update the Entityframework Dynamic Filter nuget package to 2.2 from 1.4 which Abp uses and I came across the below issues :
Method not found: 'Void EntityFramework.DynamicFilters.DynamicFilterExtensions.Filter(System.Data.Entity.DbModelBuilder, System.String, System.Linq.Expressions.Expression1<System.Func
3<!!0,!!1,Boolean>>, !!1)'.
I was trying to use :
modelBuilder.EnableFilter("UserOrg", (MyContext ctx) => !ctx.UserIsAdmin); which is supported in the latest Dynamic filter release, but due to Abp's hard dependencies i am unable to use it.
Could you please update to the latest Dynamic Filter .
I now downloaded the latest template from AspnetZero and started backward integration into my project . On doing so I came across the below issue :
Component Abp.Web.Configuration.AbpWebModuleConfiguration could not be registered. There is already a component with that name. Did you want to modify the existing component instead? If not, make sure you specify a unique name. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: Castle.MicroKernel.ComponentRegistrationException: Component Abp.Web.Configuration.AbpWebModuleConfiguration could not be registered. There is already a component with that name. Did you want to modify the existing component instead? If not, make sure you specify a unique name.
Source Error:
Line 25: );
Line 26:
Line 27: base.Application_Start(sender, e);
Line 28: }
Line 29: }
Please help
An error occurred while retrieving package metadata for 'Abp.0.10.1.2' from source 'nuget.org'. ========== Finished ========== The HTTP request to 'GET <a class="postlink" href="https://api.nuget.org/v3/registration1-gz/abp/index.json'">https://api.nuget.org/v3/registration1- ... ndex.json'</a> has timed out after 100000ms.
I am unable to upgrade.
Hi Hilkan -
I had a requirement to track changes to few property of a Entity which I am able to do overriding ApplyABpConcepts.
I am filtering the tracking by filtering the Entity name, but the problem is when I am trying to insert a record based on the calculation from previous and new values and try to insert into another table then again the ApplyAbpConcepts is called which now has the entity which I am tracking and the new entity to which I am inserting a record. As a result I am getting into an infinite loop.
Is there a way where i can stop tracking for an entity ?
Hi Hilkan -
I am now using Organization ID to group the Tenants so that these Tenants can be grouped and I can later on use to share data between them. I moved my database connection to Organization where they can input connection string to a Organization and the tenant using this organization will use the connection string.
Now I am trying to create user from one tenant into all the grouped tenant under the organization , ie. let's say I have 3 tenants in one organization and i create an user in one tenant then i am creating 2 users of the same credentials in the other two tenants.
This works fine, but I am unable to link these users programatically. I am trying to create user and link them in a single unit of work.
I am unable to figure out when you are inserting records to the UserAccounts table.
Please help
I initially started with using Organization Unit as Companies then I am frequently running into issues while extending Roles/Settings/Users/etc..
So today I reversed my approach, now I am treating each company as a Tenant and grouping them under one organization.
Is there a way I can share the same user/roles between tenants. If not I basically have to create two users for each of the tenant and link them up.
I Wish we could extend the scope of users/roles/settings to Organizations without any additional work.
0.9.6. I upgrade as soon it's released