417.3 Multi‐Tenancy of ABP framework applications - chempkovsky/CS82ANGULAR GitHub Wiki
- Please read the first article and the second one
- After generating UI for
TenantConnectionString
we addedDefault
connection string and runDbmigrator
console app.Dbmigrator
created new database and copied tenant data. So it works. - Now we are going to follow the recommendation given by Discussion of an Alternative Scenario: Every Module Manages Its Own Migration Path
- The steps described in this article are intended only to test some aspects related to the tenant. (For production stage Abp modules must be used to declare entities and contexts.)
- In the
Second
-folder of therupbes.firstapp.Domain.csproj
-project we created new Entity withoutTenantId
-property, i.e. our Entity does not implementIMultiTenant
-interface.
Click to show the code
using System;
using Volo.Abp.Domain.Entities;
namespace rupbes.firstapp.Second
{
public class SecondEntity : Entity<Guid>
{
public virtual Guid Id { get; protected set; }
public virtual int Intfld { get; protected set; }
protected SecondEntity()
{
}
public SecondEntity(Guid id, int intfld)
{
Id = id;
SetIntfld(intfld);
}
public void SetIntfld(int value)
{
Intfld = value;
}
}
}
- In the
EntityFrameworkCore
-folder of therupbes.firstapp.EntityFrameworkCore.csproj
-project we created new DB-context
Click to show the code
using Microsoft.EntityFrameworkCore;
using rupbes.firstapp.Phbk;
using rupbes.firstapp.Second;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.Modeling;
// https://abp.io/docs/latest/framework/data/entity-framework-core/migrations
// dotnet ef migrations add Initial --context secondDbContext --output-dir SecondMigrations
// dotnet ef database update --context secondDbContext
namespace rupbes.firstapp.EntityFrameworkCore
{
[ConnectionStringName("SecondConnStr")]
public class secondDbContext : AbpDbContext<secondDbContext>
{
public secondDbContext(DbContextOptions<secondDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<SecondEntity>(b =>
{
b.ToTable(firstappConsts.DbTablePrefix + "SecondEntitys", firstappConsts.DbSchema);
b.ConfigureByConvention(); //auto configure for the base class props
//...
b.HasKey(p => p.Id);
});
builder.Entity<PhbkFile>(b =>
{
b.ToTable(firstappConsts.DbTablePrefix + "PhbkFiles", firstappConsts.DbSchema);
b.ConfigureByConvention(); //auto configure for the base class props
//...
b.HasKey(p => new { p.FileId, p.FlNm });
});
}
public DbSet<PhbkFile> PhbkFileDbSet
{
get => Set<PhbkFile>();
}
public DbSet<SecondEntity> SecondEntityDbSet
{
get => Set<SecondEntity>();
}
}
}
- In the
EntityFrameworkCore
-folder of therupbes.firstapp.EntityFrameworkCore.csproj
-project we created new DB-context factory
Click to show the code
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
using System.IO;
namespace rupbes.firstapp.EntityFrameworkCore
{
internal class secondDbContextFactory : IDesignTimeDbContextFactory<secondDbContext>
{
public secondDbContext CreateDbContext(string[] args)
{
var configuration = BuildConfiguration();
firstappEfCoreEntityExtensionMappings.Configure();
var builder = new DbContextOptionsBuilder<secondDbContext>()
.UseSqlServer(configuration.GetConnectionString("SecondConnStr"));
return new secondDbContext(builder.Options);
}
private static IConfigurationRoot BuildConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), "../rupbes.firstapp.DbMigrator/"))
.AddJsonFile("appsettings.json", optional: false);
return builder.Build();
}
}
}
- we modified
appsettings.json
-file of therupbes.firstapp.DbMigrator.csproj
andrupbes.firstapp.HttpApi.Host.csproj
projects
Click to show the code
{
"ConnectionStrings": {
"Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=firstapp;Trusted_Connection=True;TrustServerCertificate=true",
"SecondConnStr": "Server=(LocalDb)\\MSSQLLocalDB;Database=sdc;Trusted_Connection=True;TrustServerCertificate=true"
},
...
}
- We modified
ConfigureServices(ServiceConfigurationContext context)
-method of thefirstappEntityFrameworkCoreModule
-class:
Click to show the code
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<firstappDbContext>(options =>
{
/* Remove "includeAllEntities: true" to create
* default repositories only for aggregate roots */
options.AddDefaultRepositories(includeAllEntities: true);
});
context.Services.AddAbpDbContext<secondDbContext>();
if (AbpStudioAnalyzeHelper.IsInAnalyzeMode)
{
return;
}
Configure<AbpDbContextOptions>(options =>
{
/* The main point to change your DBMS.
* See also firstappDbContextFactory for EF Core tooling. */
options.UseSqlServer();
});
}
- We modified
MigrateAsync()
-method of theEntityFrameworkCorefirstappDbSchemaMigrator
-class of therupbes.firstapp.EntityFrameworkCore.csproj
-project
Click to show the code
public async Task MigrateAsync()
{
/* We intentionally resolving the firstappDbContext
* from IServiceProvider (instead of directly injecting it)
* to properly get the connection string of the current tenant in the
* current scope.
*/
await _serviceProvider
.GetRequiredService<firstappDbContext>()
.Database
.MigrateAsync();
await _serviceProvider
.GetRequiredService<secondDbContext>()
.Database
.MigrateAsync();
}
- In thw windows terminal we run the command
Click to show the code
D:\Development\rupbes.firstapp\src\rupbes.firstapp.EntityFrameworkCore>dotnet ef migrations add Initial --context secondDbContext --output-dir SecondMigrations
Build started...
Build succeeded.
Done. To undo this action, use 'ef migrations remove'
- we run
rupbes.firstapp.DbMigrator.csproj
-console app to createHost
-database. It works. (New database was created)
- Using generated UI we added new row for the Tenant
Click to show the picture
- we run
rupbes.firstapp.DbMigrator.csproj
-console app to createTenant
-database. It works. (New database was created)