Published on

Add a new Module to the Tiered ABP App with separate database for the module

4 min read
Table of Contents

Add a new Module to the Tiered ABP App with a separate database for the module

In this post, we will see how to develop a modular with a tiered ABP application. We will add a new module to the tiered ABP app and then use the separate database to store the module's data and identity data.

Creating the ABP application and running migrations

abp new MainApp -t app -u mvc --tiered

Run Migrations

Change directory to src/MainApp.DbMigrator and run the migration project

dotnet run

This will apply the migrations to the DB and we can run the MainApp.Web project. This will host the UI and API..

Add a new Module

Now we will add a new module to our MainApp

abp add-module ModuleA --new --add-to-solution-file

This command will create a new module and add the new module to the solution.

Now you can run all their host and see the API and UI available in the app.

Add a new Entity to the ModuleA

We will create a new Entity inside the MainApp.ModuleA.Domain called TodoOne

1. Create an Entity

The first step is to create an Entity. Create the Entity in the MainApp.ModuleA.Domain project.

public class TodoOne : Entity<Guid>
{
    public string Content { get; set; }
    public bool IsDone { get; set; }
}

2. Add Entity to ef core

Next is to add Entity to the EF Core. we will find the DbContext in the MainApp.ModuleA.EntityFrameworkCore project. Add the DbSet to the DbContext

public DbSet<TodoOne> TodoOnes { get; set; }

3. Configure Entity in ef core

Configuration is done in the DbContextModelCreatingExtensions class. This should be available in the MainApp.ModuleA.EntityFrameworkCore project

builder.Entity<TodoOne>(b =>
{
    b.ToTable(options.TablePrefix + "TodoOnes", options.Schema);
    b.ConfigureByConvention(); //auto configure for the base class props
});

4. Adding Migrations for the ModuleA

Now that the Entity is configured we can add the migrations.

Create EntityFrameworkCore\ModuleA folder in the MainApp.HttpApi.Host project.

Create a ModuleAHttpApiHostMigrationsDbContext.cs file in the EntityFrameworkCore\ModuleA folder

public class ModuleAHttpApiHostMigrationsDbContext : AbpDbContext<ModuleAHttpApiHostMigrationsDbContext>
{
    public ModuleAHttpApiHostMigrationsDbContext(DbContextOptions<ModuleAHttpApiHostMigrationsDbContext> options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.ConfigureModuleA();
    }
}

Create a ModuleAHttpApiHostMigrationsDbContextFactory.cs file in the EntityFrameworkCore\ModuleA folder

public class ModuleAHttpApiHostMigrationsDbContextFactory : IDesignTimeDbContextFactory<ModuleAHttpApiHostMigrationsDbContext>
{
    public ModuleAHttpApiHostMigrationsDbContext CreateDbContext(string[] args)
    {
        var configuration = BuildConfiguration();

        var builder = new DbContextOptionsBuilder<ModuleAHttpApiHostMigrationsDbContext>()
            .UseSqlServer(configuration.GetConnectionString("ModuleA"));
        return new ModuleAHttpApiHostMigrationsDbContext(builder.Options);
    }

    private static IConfigurationRoot BuildConfiguration()
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false);

        return builder.Build();
    }
}

Update the connection string in appsettings.json in the MainApp.HttpApi.Host

  "ConnectionStrings": {
    "Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=MainApp;Trusted_Connection=True",
    "ModuleA": "Server=(LocalDb)\\MSSQLLocalDB;Database=ModuleA;Trusted_Connection=True",
  },

To create migration run this command:

dotnet ef migrations add created_todoone --context ModuleAHttpApiHostMigrationsDbContext --output-dir Migrations/ModuleA

Verify the migrations created in the migrations folder.

To update the database run this command

dotnet ef database update --context ModuleAHttpApiHostMigrationsDbContext

5. Create an Entity Dto

Dto are placed in MainApp.ModuleA.Application.Contracts project

public class TodoOneDto : EntityDto<Guid>
{
    public string Content { get; set; }
    public bool IsDone { get; set; }
}

6. Map Entity to Dto

Abp uses AutoMapper to map Entity to Dto. you can find the ApplicationAutoMapperProfile file which is used by the AutoMapper in the MainApp.ModuleA.Application project.

CreateMap<TodoOne, TodoOneDto>();
CreateMap<TodoOneDto, TodoOne>();

7. Create an Application Services

Application services are created in the MainApp.ModuleA.Application project

public class TodoOneAppService : ModuleAAppService
{
    private readonly IRepository<TodoOne, Guid> todoOneRepository;

    public TodoOneAppService(IRepository<TodoOne, Guid> todoOneRepository)
    {
        this.todoOneRepository = todoOneRepository;
    }

    public async Task<List<TodoOneDto>> GetAll()
    {
        return ObjectMapper.Map<List<TodoOne>, List<TodoOneDto>>(await todoOneRepository.GetListAsync());
    }

    public async Task<TodoOneDto> CreateAsync(TodoOneDto todoOneDto)
    {
        var TodoOne = ObjectMapper.Map<TodoOneDto, TodoOne>(todoOneDto);
        var createdTodoOne = await todoOneRepository.InsertAsync(TodoOne);
        return ObjectMapper.Map<TodoOne, TodoOneDto>(createdTodoOne);
    }

    public async Task<TodoOneDto> UpdateAsync(TodoOneDto todoOneDto)
    {
        var TodoOne = ObjectMapper.Map<TodoOneDto, TodoOne>(todoOneDto);
        var createdTodoOne = await todoOneRepository.UpdateAsync(TodoOne);
        return ObjectMapper.Map<TodoOne, TodoOneDto>(createdTodoOne);
    }

    public async Task<bool> DeleteAsync(Guid id)
    {
        var TodoOne = await todoOneRepository.FirstOrDefaultAsync(x=> x.Id == id);
        if(TodoOne != null)
        {
            await todoOneRepository.DeleteAsync(TodoOne);
            return true;
        }
        return false;
    }
}

8. Update AddAbpDbContext method in the ModuleAEntityFrameworkCoreModule

options.AddDefaultRepositories(includeAllEntities: true);

9. Update the ConfigureAutoApiControllers in the MainAppHttpApiHostModule in the MainApp.HttpApi.Host

Configure<AbpAspNetCoreMvcOptions>(options =>
            {
                options.ConventionalControllers.Create(typeof(ModuleAApplicationModule).Assembly);
            });

Repo: https://github.com/antosubash/NewModuleWithTieredAbpApp