422.1 Abp microserices and Multi‐Tenancy - chempkovsky/CS82ANGULAR GitHub Wiki
- We are using Abp community edition
- We discussed
Multi-tenancyin articles - We used the
XXX.DbMigrator.csprojproject to create database schemas for each tenant that has a separate database connection string. - Now it's time to look into the code of the
XXX.DbMigrator.csprojproject and create something similar for the microservices solution.- First we will show that
XXX.DbMigrator.csprojis only suitable for development phase, even for monolithic applications. - Finally, we will create a basic console application (similar to the
XXX.DbMigrator.csprojproject) that can be used in production for microservices applications.
- First we will show that
- We are currently using:
- MS Visual studio 22 (Community Edition)
-
Volo.Abp.Studio.Cli v.1.2.1 Community Edition
abp cli check-version
-
Volo.Abp.Cli v.9.3.1 Community Edition
- goto here to check the latest version
- ABP Studio v.1.2.1 Community Edition
- Redis v=7.0.15
- Let's create new app with a command
abp new rupbes.testapp --template app --ui-framework angular --theme leptonx-lite --database-provider ef --database-management-system SqlServer --no-tests --create-solution-folder
- open
testappDbMigrationService.cs-file of therupbes.testapp.Domain.csproj-project- please take a look at the
GetEntityFrameworkCoreProjectFolderPath()andGetSolutionDirectoryPath()-methods:
- please take a look at the
Click to show the code
private string? GetEntityFrameworkCoreProjectFolderPath()
{
var slnDirectoryPath = GetSolutionDirectoryPath();
if (slnDirectoryPath == null)
{
throw new Exception("Solution folder not found!");
}
var srcDirectoryPath = Path.Combine(slnDirectoryPath, "src");
return Directory.GetDirectories(srcDirectoryPath)
.FirstOrDefault(d => d.EndsWith(".EntityFrameworkCore"));
}
private string? GetSolutionDirectoryPath()
{
var currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());
while (currentDirectory != null && Directory.GetParent(currentDirectory.FullName) != null)
{
currentDirectory = Directory.GetParent(currentDirectory.FullName);
if (currentDirectory != null && Directory.GetFiles(currentDirectory.FullName).FirstOrDefault(f => f.EndsWith(".sln")) != null)
{
return currentDirectory.FullName;
}
}
return null;
}- So, the default implementation of DbMigrationService can only be used by developers.
- Suppose we have a folder E:\Development
- start cmd.exe and run the commands
E:
cd E:\Development
abp new rupbes.portal --template module --database-provider ef --database-management-system SqlServer --create-solution-folder --version 9.3.1 --old
- We ignore all errors
- Launch Abp Studio Community Edition and delete the following projects (as we are not working with Blazor and MongoDB)
rupbes.portal.Blazor.Host.Clientrupbes.portal.Blazor.Server.Hostrupbes.portal.Blazor.Hostrupbes.portal.Blazorrupbes.portal.Blazor.Serverrupbes.portal.Blazor.WebAssemblyrupbes.portal.Blazor.WebAssembly.Bundlingrupbes.portal.MongoDBrupbes.portal.Web.Unified
- open
appsettings.jsonof therupbes.portal.HttpApi.Host.csproj-project. "ConnectionStrings" consists of two strings: "Default" and "portal".
{
"ConnectionStrings": {
"Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=portal_Main;Trusted_Connection=True;TrustServerCertificate=True",
"portal": "Server=(LocalDb)\\MSSQLLocalDB;Database=portal_Module;Trusted_Connection=True;TrustServerCertificate=True"
}
}- The "Default" connection string will be used by the DBContext to manage tables of the following modules:
- Permission
- Setting
- AuditLogging
- Identity
- Feature
- Tenant
- "portal" connection string will be used by the DBContext of application layer.
- Launch ABP Studio community edition and open
rupbes.portal-solution- right click
rupbes.portal/host-folder and chooseAdd/Package/New Packagemenu item - In the dialog select
C# console application- Set "Package name" =
rupbes.portal.DbMigrator - Click
Create-button
- Set "Package name" =
- right click
- Open solution with Visual Studio and modify
rupbes.portal.DbMigrator.csproj-file as follows:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="2.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Autofac" Version="9.3.1" />
</ItemGroup>
</Project>- We removed
<ImplicitUsings>enable</ImplicitUsings>from the<PropertyGroup>...</PropertyGroup>and we added two<ItemGroup>...</ItemGroup>.
- open
Program.cs-file of therupbes.portal.DbMigrator.csproj-project and modify as follows:
Click to show the code
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Events;
namespace rupbes.portal.DbMigrator;
class Program
{
static async Task Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("Volo.Abp", LogEventLevel.Warning)
#if DEBUG
.MinimumLevel.Override("rupbes.testapp", LogEventLevel.Debug)
#else
.MinimumLevel.Override("rupbes.testapp", LogEventLevel.Information)
#endif
.Enrich.FromLogContext()
.WriteTo.Async(c => c.File("Logs/logs.txt"))
.WriteTo.Async(c => c.Console())
.CreateLogger();
await CreateHostBuilder(args).RunConsoleAsync();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.AddAppSettingsSecretsJson()
.ConfigureLogging((context, logging) => logging.ClearProviders())
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<DbMigratorHostedService>();
});
}- We just copied the content of the
Program.cs-file from the project and replaced the namespace withnamespace rupbes.portal.DbMigrator;-
services.AddHostedService<DbMigratorHostedService>();is the only line of code that is still wrong.
-
- Add
appsettings.jsonto DbMigrator package
{
"ConnectionStrings": {
"Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=portal_Main;Trusted_Connection=True;TrustServerCertificate=True",
"portal": "Server=(LocalDb)\\MSSQLLocalDB;Database=portal_Module;Trusted_Connection=True;TrustServerCertificate=True"
}
}- Add
appsettings.secrets.jsonto DbMigrator package
{
}- modify
rupbes.portal.DbMigrator.csproj-file as follows:
<Project Sdk="Microsoft.NET.Sdk">
...
<ItemGroup>
<None Remove="appsettings.json" />
<None Remove="appsettings.secrets.json" />
</ItemGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="appsettings.secrets.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>- Add a
DbMigratorHostedService-class as follows:
Click to show the code
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using rupbes.portal.DbMigrator.Data;
using Serilog;
using Volo.Abp;
using Volo.Abp.Data;
namespace rupbes.portal.DbMigrator;
public class DbMigratorHostedService : IHostedService
{
private readonly IHostApplicationLifetime _hostApplicationLifetime;
private readonly IConfiguration _configuration;
public DbMigratorHostedService(IHostApplicationLifetime hostApplicationLifetime, IConfiguration configuration)
{
_hostApplicationLifetime = hostApplicationLifetime;
_configuration = configuration;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
using (var application = await AbpApplicationFactory.CreateAsync<DbMigratorModule>(options =>
{
options.Services.ReplaceConfiguration(_configuration);
options.UseAutofac();
options.Services.AddLogging(c => c.AddSerilog());
options.AddDataMigrationEnvironment();
}))
{
await application.InitializeAsync();
await application
.ServiceProvider
.GetRequiredService<DbMigrationService>()
.MigrateAsync();
await application.ShutdownAsync();
_hostApplicationLifetime.StopApplication();
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}- We just copied the content of the
...HostedService.cs-file from the project , we replaced the namespace withnamespace rupbes.portal.DbMigrator;and replacedusingwithusing rupbes.portal.DbMigrator.Data; - We also changed the names:
DbMigratorModuleandDbMigrationService
-
DbMigratorHostedService-class referencesDbMigratorModule-class. Let's createDbMigratorModule- Add the
DbMigratorModule-class to therupbes.testapp.DbMigrator.csproj-project as follows:
- Add the
Click to show the code
using Microsoft.Extensions.DependencyInjection;
using rupbes.portal.DbMigrator.EntityFrameworkCore;
using rupbes.portal.EntityFrameworkCore;
using Volo.Abp.AuditLogging.EntityFrameworkCore;
using Volo.Abp.Autofac;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.SqlServer;
using Volo.Abp.FeatureManagement.EntityFrameworkCore;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.Modularity;
using Volo.Abp.OpenIddict.EntityFrameworkCore;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.SettingManagement.EntityFrameworkCore;
using Volo.Abp.TenantManagement.EntityFrameworkCore;
namespace rupbes.portal.DbMigrator
{
[DependsOn(
typeof(AbpAutofacModule),
typeof(portalEntityFrameworkCoreModule),
typeof(AbpOpenIddictEntityFrameworkCoreModule),
typeof(AbpAuditLoggingEntityFrameworkCoreModule),
typeof(AbpTenantManagementEntityFrameworkCoreModule),
typeof(AbpFeatureManagementEntityFrameworkCoreModule),
typeof(AbpPermissionManagementEntityFrameworkCoreModule),
typeof(AbpIdentityEntityFrameworkCoreModule),
typeof(AbpSettingManagementEntityFrameworkCoreModule),
typeof(AbpEntityFrameworkCoreSqlServerModule)
)]
public class DbMigratorModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
base.ConfigureServices(context);
context.Services.AddAbpDbContext<defaultMigratorDbContext>(options =>
{
options.AddDefaultRepositories(includeAllEntities: true);
/* Add custom repositories here. Example:
* options.AddRepository<Question, EfCoreQuestionRepository>();
*/
});
context.Services.AddAbpDbContext<portalMigratorDbContext>(options =>
{
options.AddDefaultRepositories(includeAllEntities: true);
/* Add custom repositories here. Example:
* options.AddRepository<Question, EfCoreQuestionRepository>();
*/
});
Configure<AbpDbContextOptions>(options =>
{
options.UseSqlServer();
});
/*
PostConfigure<AbpDataSeedOptions>(options =>
{
options.Contributors.RemoveAll(x => x == typeof(IdentityDataSeedContributor));
});
*/
}
}
}- Also we have to modify
rupbes.portal.DbMigrator.csproj-file as follows:
<Project Sdk="Microsoft.NET.Sdk">
...
<ItemGroup>
<ProjectReference Include="../../src/rupbes.portal.EntityFrameworkCore/rupbes.portal.EntityFrameworkCore.csproj" />
<ProjectReference Include="../rupbes.portal.Host.Shared/rupbes.portal.Host.Shared.csproj" />
</ItemGroup>
</Project>- Create
EntityFrameworkCore-folder in therupbes.portal.DbMigrator.csproj-project- In the
EntityFrameworkCore-folder createportalMigratorDbContext-class as follows
- In the
Click to show the code
using Microsoft.EntityFrameworkCore;
using rupbes.portal.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
namespace rupbes.portal.DbMigrator.EntityFrameworkCore
{
[ConnectionStringName("portal")]
public class portalMigratorDbContext : AbpDbContext<portalMigratorDbContext>
{
public portalMigratorDbContext(DbContextOptions<portalMigratorDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configureportal();
}
}
}- In the
EntityFrameworkCore-folder createportalMigratorDbContextFactory-class as follows
Click to show the code
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
namespace rupbes.portal.DbMigrator.EntityFrameworkCore
{
public class portalMigratorDbContextFactory : IDesignTimeDbContextFactory<portalMigratorDbContext>
{
public portalMigratorDbContext CreateDbContext(string[] args)
{
var configuration = BuildConfiguration();
var builder = new DbContextOptionsBuilder<portalMigratorDbContext>()
.UseSqlServer(configuration.GetConnectionString("portal"));
return new portalMigratorDbContext(builder.Options);
}
private static IConfigurationRoot BuildConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false);
return builder.Build();
}
}
}- It requires modification of the
rupbes.portal.DbMigrator.csproj-file as follows:
<Project Sdk="Microsoft.NET.Sdk">
...
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.5" />
<PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="9.3.1" />
<PackageReference Include="Volo.Abp.EntityFrameworkCore.SqlServer" Version="9.3.1" />
</ItemGroup>
</Project>In the EntityFrameworkCore-folder create defaultMigratorDbContext-class as follows:
Click to show the code
using Microsoft.EntityFrameworkCore;
using Volo.Abp.AuditLogging.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.FeatureManagement.EntityFrameworkCore;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.OpenIddict.EntityFrameworkCore;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.SettingManagement.EntityFrameworkCore;
using Volo.Abp.TenantManagement.EntityFrameworkCore;
namespace rupbes.portal.DbMigrator.EntityFrameworkCore
{
[ConnectionStringName("Default")]
public class defaultMigratorDbContext : AbpDbContext<defaultMigratorDbContext>
{
public defaultMigratorDbContext(DbContextOptions<defaultMigratorDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ConfigurePermissionManagement();
modelBuilder.ConfigureSettingManagement();
modelBuilder.ConfigureAuditLogging();
modelBuilder.ConfigureIdentity();
modelBuilder.ConfigureOpenIddict(); // must be removed for Keycloak
modelBuilder.ConfigureFeatureManagement();
modelBuilder.ConfigureTenantManagement();
}
}
}- It requires modification of the
rupbes.portal.DbMigrator.csproj-file as follows:
<Project Sdk="Microsoft.NET.Sdk">
...
<ItemGroup>
<PackageReference Include="Volo.Abp.PermissionManagement.EntityFrameworkCore" Version="9.3.1" />
<PackageReference Include="Volo.Abp.AuditLogging.EntityFrameworkCore" Version="9.3.1" />
<PackageReference Include="Volo.Abp.SettingManagement.EntityFrameworkCore" Version="9.3.1" />
<PackageReference Include="Volo.Abp.FeatureManagement.EntityFrameworkCore" Version="9.3.1" />
<PackageReference Include="Volo.Abp.Identity.EntityFrameworkCore" Version="9.3.1" />
<PackageReference Include="Volo.Abp.OpenIddict.EntityFrameworkCore" Version="9.3.1" />
<PackageReference Include="Volo.Abp.TenantManagement.EntityFrameworkCore" Version="9.3.1" />
<PackageReference Include="Volo.Abp.TenantManagement.Application" Version="9.3.1" />
</ItemGroup>
</Project>- In the
EntityFrameworkCore-folder createdefaultMigratorDbContextFactory-class as follows
Click to show the code
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
namespace rupbes.portal.DbMigrator.EntityFrameworkCore
{
public class defaultMigratorDbContextFactory : IDesignTimeDbContextFactory<defaultMigratorDbContext>
{
public defaultMigratorDbContext CreateDbContext(string[] args)
{
var configuration = BuildConfiguration();
var builder = new DbContextOptionsBuilder<defaultMigratorDbContext>()
.UseSqlServer(configuration.GetConnectionString("Default"));
return new defaultMigratorDbContext(builder.Options);
}
private static IConfigurationRoot BuildConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false);
return builder.Build();
}
}
}- Create
Data-folder in therupbes.portal.DbMigrator.csproj-project- In the
Data-folder createIDbSchemaMigrator-interface as follows:
- In the
Click to show the code
namespace rupbes.portal.DbMigrator.Data
{
public interface IDbSchemaMigrator
{
Task MigrateAsync();
}
}- In the
Data-folder createDbSchemaMigrator-class as follows:
Click to show the code
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using rupbes.portal.DbMigrator.EntityFrameworkCore;
using Volo.Abp.DependencyInjection;
namespace rupbes.portal.DbMigrator.Data
{
public class DbSchemaMigrator : IDbSchemaMigrator, ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public DbSchemaMigrator(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task MigrateAsync()
{
/* We intentionally resolving the testappDbContext
* from IServiceProvider (instead of directly injecting it)
* to properly get the connection string of the current tenant in the
* current scope.
*/
await _serviceProvider
.GetRequiredService<defaultMigratorDbContext>()
.Database
.MigrateAsync();
await _serviceProvider
.GetRequiredService<portalMigratorDbContext>()
.Database
.MigrateAsync();
}
}
}- Create
Data-folder in therupbes.portal.DbMigrator.csproj-project- In the
Data-folder createDbMigrationService-class as follows:
- In the
Click to show the code
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using rupbes.portal.MultiTenancy;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.TenantManagement;
namespace rupbes.portal.DbMigrator.Data
{
public class DbMigrationService : ITransientDependency
{
public ILogger<DbMigrationService> Logger { get; set; }
private readonly IDataSeeder _dataSeeder;
private readonly IEnumerable<IDbSchemaMigrator> _dbSchemaMigrators;
private readonly ITenantRepository _tenantRepository;
private readonly ICurrentTenant _currentTenant;
public DbMigrationService(
IDataSeeder dataSeeder,
ITenantRepository tenantRepository,
ICurrentTenant currentTenant,
IEnumerable<IDbSchemaMigrator> dbSchemaMigrators)
{
_dataSeeder = dataSeeder;
_tenantRepository = tenantRepository;
_currentTenant = currentTenant;
_dbSchemaMigrators = dbSchemaMigrators;
Logger = NullLogger<DbMigrationService>.Instance;
}
public async Task MigrateAsync()
{
Logger.LogInformation("Started database migrations...");
await MigrateDatabaseSchemaAsync();
await SeedDataAsync();
Logger.LogInformation($"Successfully completed host database migrations.");
if (MultiTenancyConsts.IsEnabled)
{
var tenants = await _tenantRepository.GetListAsync(includeDetails: true);
var migratedDatabaseSchemas = new HashSet<string>();
foreach (var tenant in tenants)
{
using (_currentTenant.Change(tenant.Id))
{
if (tenant.ConnectionStrings.Any())
{
var tenantConnectionStrings = tenant.ConnectionStrings
.Select(x => x.Value)
.ToList();
if (!migratedDatabaseSchemas.IsSupersetOf(tenantConnectionStrings))
{
await MigrateDatabaseSchemaAsync(tenant);
migratedDatabaseSchemas.AddIfNotContains(tenantConnectionStrings);
}
}
await SeedDataAsync(tenant);
}
Logger.LogInformation($"Successfully completed {tenant.Name} tenant database migrations.");
}
Logger.LogInformation("Successfully completed all database migrations.");
}
Logger.LogInformation("You can safely end this process...");
}
private async Task MigrateDatabaseSchemaAsync(Tenant? tenant = null)
{
Logger.LogInformation(
$"Migrating schema for {(tenant == null ? "host" : tenant.Name + " tenant")} database...");
foreach (var migrator in _dbSchemaMigrators)
{
await migrator.MigrateAsync();
}
}
private async Task SeedDataAsync(Tenant? tenant = null)
{
Logger.LogInformation($"Executing {(tenant == null ? "host" : tenant.Name + " tenant")} database seed...");
await _dataSeeder.SeedAsync(new DataSeedContext(tenant?.Id)
.WithProperty(IdentityDataSeedContributor.AdminEmailPropertyName,
IdentityDataSeedContributor.AdminEmailDefaultValue)
.WithProperty(IdentityDataSeedContributor.AdminPasswordPropertyName,
IdentityDataSeedContributor.AdminPasswordDefaultValue)
);
}
}
}- Launch
cmd.exeand run the following commands
e:
cd E:\development\rupbes.portal\host\rupbes.portal.DbMigrator
dotnet ef migrations add Initial --context portalMigratorDbContext --output-dir Migrations/portal
dotnet ef migrations add Initial --context defaultMigratorDbContext --output-dir Migrations/default
- build and launch
rupbes.portal.DbMigrator-console application. Two databases will be crated.
- We have discussed DataSeeders in previous articles.
- Let's say we need to add Abp CmsKit to the solution.
- We launch
cmd.exeand run the following commands:
- We launch
e:
cd E:\development\rupbes.portal
abp add-module Volo.CmsKit --skip-db-migrations
- Next we add
portalGlobalFeatureConfigurator-class intorupbes.portal.Domain.Shared.csproj-project.-
cmsKit.EnableAll();- just for example
-
Click to show the code
using Volo.Abp.GlobalFeatures;
using Volo.Abp.Threading;
namespace rupbes.portal
{
public static class portalGlobalFeatureConfigurator
{
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
public static void Configure()
{
OneTimeRunner.Run(() =>
{
/* You can configure (enable/disable) global features of the used modules here.
* Please refer to the documentation to learn more about the Global Features System:
* https://docs.abp.io/en/abp/latest/Global-Features
*/
});
GlobalFeatureManager.Instance.Modules.CmsKit(cmsKit =>
{
cmsKit.EnableAll();
});
}
}
}- In
portalDomainSharedModule.cs-class ofrupbes.portal.Domain.Shared.csproj-project we addPreConfigureServices- method
Click to show the code
public override void PreConfigureServices(ServiceConfigurationContext context)
{
portalGlobalFeatureConfigurator.Configure();
}In the CreateDbContext()-method of portalMigratorDbContextFactory.cs-class of rupbes.portal.DbMigrator.csproj-project we add a line of code:
Click to show the code
public portalMigratorDbContext CreateDbContext(string[] args)
{
var configuration = BuildConfiguration();
portalGlobalFeatureConfigurator.Configure();
var builder = new DbContextOptionsBuilder<portalMigratorDbContext>()
.UseSqlServer(configuration.GetConnectionString("portal"));
return new portalMigratorDbContext(builder.Options);
}In DbMigratorModule.cs-class of rupbes.portal.DbMigrator.csproj-project we add a method
Click to show the code
public override void PreConfigureServices(ServiceConfigurationContext context)
{
portalGlobalFeatureConfigurator.Configure();
}In OnModelCreating(ModelBuilder modelBuilder)-method of portalMigratorDbContext.cs-class of rupbes.portal.DbMigrator.csproj-project we add a line of code
Click to show the code
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configureportal();
modelBuilder.ConfigureCmsKit();
}- Read this first
- In
appsettings.jsonofrupbes.portal.DbMigrator.csproj-project we add "CmsKit"-connection string which is equal to "portal"-connection string
- In
{
"ConnectionStrings": {
"Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=portal_Main;Trusted_Connection=True;TrustServerCertificate=True",
"portal": "Server=(LocalDb)\\MSSQLLocalDB;Database=portal_Module;Trusted_Connection=True;TrustServerCertificate=True",
"CmsKit": "Server=(LocalDb)\\MSSQLLocalDB;Database=portal_Module;Trusted_Connection=True;TrustServerCertificate=True"
}
}- Launch
cmd.exeand run the following commands
e:
cd E:\development\rupbes.portal\host\rupbes.portal.DbMigrator
dotnet ef migrations add CmsKitAdded --context portalMigratorDbContext --output-dir Migrations/portal
- build and launch
rupbes.portal.DbMigrator-console application. CmsKit tables will be crated.