Base solution for your next web application
Open Closed

Create New Tenant error on Admin #2169


User avatar
0
dirtman created

Hi, I run a Fresh codes with ASP.NET Core on my local machine and tried to Create New Tenant with a Multiple Database. but turned out it returns an internal server error on this code

//TenantManager.cs line [#91](https://support.aspnetzero.com/QA/Questions/91)
CheckErrors(await _roleManager.CreateStaticRoles(tenant.Id));

The database was successfully created, it's stuck on that line.

regards, Irvan


4 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Is MSDTC windows service is enabled and running on your machine ? If not, you must enable it.

    Otherwise, can you check Logs.txt file under for web project for a detailed error message ?

    Thanks.

  • User Avatar
    0
    dirtman created

    Thanks for reply, Enabling MSDTC works fine in my locahost. I went to Services.msc -> Distributed Transaction Coordinator and start the service.

    however, I'm using Azure SQL Database on my production. and I believe they are not supporting MSDTC. do you have any thoughts how to make this works on production?

  • User Avatar
    0
    JeffMH created

    Distributed transactions are now supported in AzureSQL.

    <a class="postlink" href="https://azure.microsoft.com/en-us/blog/elastic-database-transactions-with-azure-sql-database/">https://azure.microsoft.com/en-us/blog/ ... -database/</a>

    That's the first article I found googling but as long as you are on framework 4.6.1, you should be good to go. You may also need to be running on the latest version of AzureSQL as well, in case your instances are not setup to auto migrate to the latest.

    But, you will need to modify the CreateTenant code to create the database manually. The built in create database that EF migrations use does not work on AzureSQL. I had to write code in the CreateTenant that executed a T-SQL Create Database command when running on azure.

    Below is the code. FYI, I store two app settings inside the web.config file.

    &lt;add key=&quot;MyPortal.IsAzureSql&quot; value=&quot;false&quot; /&gt;
    &lt;add key=&quot;MyPortal.AzureSqlDbCreateScript&quot; value=&quot;CREATE DATABASE [{0}] ( EDITION = &#39;basic&#39; )&quot; /&gt;
    

    You can customize the script to create for what you need. We have different scripts depending on where we deploy so app settings work well for us since we can use config overrides on build.

    AzureDbCreator. This handles all the legwork on creating the database. Modify TenantManager to have this injected into the constructor.

    public class AzureDbCreator
    {
    	public void CreateDatabase(string tenantConnection)
    	{
    		//Need to parse the connection string in order to create the DB.
    		//Workflow:
    		//  1.  Get Connection String
    		//  2.  Parse the name of the database from the connection string.
    		//  3.  Change to connect to the Master Database
    		//  4.  Connect and create the database.
    
    		//  1.  Get Connection String
    		var builder = new SqlConnectionStringBuilder(tenantConnection);
    
    		//  2. Parse the name
    		var dbName = builder.InitialCatalog;
    
    		if (string.IsNullOrEmpty(dbName))
    		{
    			throw new AzureDbCreateException("Unable to parse the connection string and find the name of the Database", tenantConnection);
    		}
    
    		// 3.  Change the connection to Master
    		builder.InitialCatalog = "master";
    
    		// 4.  Create Database
    		try
    		{
    			CreateDatabase(builder.ConnectionString, dbName);
    		}
    		catch (Exception ex)
    		{
    			throw new AzureDbCreateException("Unable to create the database", tenantConnection, ex);
    		}
    	}
    
    	/// <summary>
    	/// Try to connect to the DB
    	/// </summary>
    	/// <param name="connectionString"></param>
    	/// <returns></returns>
    	public bool TryConnect(string connectionString)
    	{
    		bool retVal;
    
    		try
    		{
    			using (var connection = new SqlConnection(connectionString))
    			{
    				connection.Open();
    
    				retVal = true;
    
    				connection.Close();
    			}
    		}
    		catch (Exception)
    		{
    			retVal = false;
    		}
    
    		return retVal;
    	}
    
    	private void CreateDatabase(string connectionString, string dbName)
    	{
    		//First find the template Sql for creating the Database
    		using (var connection = new SqlConnection(connectionString))
    		{
    			var createScript = ConfigurationManager.AppSettings[MyPortalAppSettings.AzureSqlDbCreateScript];
    
    			//Create the command and set its properties.
    			var command = new SqlCommand
    			{
    				Connection = connection,
    				CommandText = string.Format(createScript, dbName),
    				CommandType = CommandType.Text,
    				CommandTimeout = 120 //Seconds
    			};
    
    			command.Parameters.AddWithValue("@p1", dbName);
    
    			// Open the connection and execute the reader.
    			connection.Open();
    
    			var result = command.ExecuteNonQuery();
    		}
    	}
    }
    

    And inside the tenant manager I modified the CreateWithAdminUserAsync method. All the other code in the method is the same as what you download.

    public async Task<int> CreateWithAdminUserAsync(string tenancyName, string name, string adminPassword, string adminEmailAddress, string connectionString, bool isActive, int? editionId, bool shouldChangePasswordOnNextLogin, bool sendActivationEmail)
            {
                int newTenantId;
                long newAdminId;
    
                //M3 - If this is azure, I need to first make sure the database is created before moving on.  If it isn't, let's create the DB first.  
                //M3 - This cannot be within a transaction with azure.
                //M3 - Need to check if we are running in Azure.  if so, we need to create the database first.
                var isAzureSql = Convert.ToBoolean(ConfigurationManager.AppSettings[MyPortalAppSettings.IsAzureSql]);
    
                if (isAzureSql)
                {
                    if(!_azureDbCreator.TryConnect(connectionString))
                        _azureDbCreator.CreateDatabase(connectionString);
                }
    
                using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.RequiresNew))
                {
    
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Thanks @JeffMH, this is fantastic :). Thanks again for information you have provided.