• SajanTech's
  • Posts
  • Integrating ABP Modules in Your ASP.NET Core Web API Project. A Step-by-Step Guide

Integrating ABP Modules in Your ASP.NET Core Web API Project. A Step-by-Step Guide

Introduction:
In this post, I’ll show you how to integrate ABP modules into your ASP.NET Core Web API project from scratch, without using the ABP solution template. Whether you’re starting a new project or enhancing an existing one, this guide will help you make the most of ABP modules while keeping your setup fully customizable.

Prerequisites:
Before you get started, make sure you have the following:

  • .NET Core SDK 8 or later installed.

  • The ABP CLI tool for managing ABP modules.

  • Docker installed and set up to run a local PostgreSQL database.

  • An IDE of your choice (e.g., Visual Studio, Visual Studio Code, or JetBrains Rider).

These tools are essential to successfully follow along with this guide.

FYI: You can find the above prerequisites links:
- .NET Core sdk https://dotnet.microsoft.com/en-us/download
- Docker https://www.docker.com/

Let’s Get Started

If you’ve already installed the ABP CLI tools, feel free to skip the following setup step.

To install the ABP Studio CLI, run the command:

dotnet tool install -g Volo.Abp.Studio.Cli

This command will install the ABP Studio CLI on your machine.

Next, let’s create a simple Web API project using the .NET CLI

dotnet new webapi -n Simple.TodoApp

This will generate a minimal API project with a default WeatherForecast endpoint. Since we won’t be using it, you can go ahead and delete all related files and code.

Your Program.cs file should now look like this

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

app.UseHttpsRedirection();

app.Run();

Let’s dive into the step-by-step process of integrating ABP modules.

Step 1: Add Required ABP Module Packages

To start, we need to add some essential packages from the ABP modules. In this guide, we’ll use the .NET CLI to install them, but feel free to use the NuGet Package Manager in your IDE if you prefer.

Here are the packages you’ll need:

dotnet add package Volo.Abp.Core --version 9.0.3
dotnet add package Volo.Abp.Autofac --version 9.0.3
dotnet add package Volo.Abp.AspNetCore --version 9.0.3
dotnet add package Volo.Abp.AspNetCore.Mvc --version 9.0.3
dotnet add package Volo.Abp.Swashbuckle --version 9.0.3
dotnet add package Volo.Abp.EntityFrameworkCore --version 9.0.3
dotnet add package Volo.Abp.EntityFrameworkCore.PostgreSql --version 9.0.3

Now, create a new file named ApiModule.cs. This will serve as the main module for our project. Paste the following code into the file:

using Volo.Abp;
using Volo.Abp.AspNetCore;
using Volo.Abp.Autofac;
using Volo.Abp.Modularity;

namespace Simple.TodoApp;

[DependsOn(typeof(AbpAspNetCoreModule))]
[DependsOn(typeof(AbpAutofacModule))]
public class ApiModule : AbpModule
{

    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var app = context.GetApplicationBuilder();
        var env = context.GetEnvironment();
        if(env.IsDevelopment()) {
            app.UseHsts();
        } else {
            app.UseHttpsRedirection();
        }

        app.UseRouting();
        app.UseConfiguredEndpoints(opt => {
            opt.MapControllers();
            opt.MapOpenApi();
        });
    }
}

We also need to update the Program.cs file accordingly. Because this is where our program class integrate to the ABP Module system.

using Simple.TodoApp;

var builder = WebApplication.CreateBuilder(args);
builder.Host.UseAutofac();

await builder.AddApplicationAsync<ApiModule>();

var app  = builder.Build();

await app.InitializeApplicationAsync();
await app.RunAsync();

The AddApplicationAsync method is responsible for registering all the services defined in the modules. It then initializes and starts the application.

Step 2: Configure the PostgreSQL Database

In Step 1, you installed the Entity Framework Core packages. Now, we need to add them as dependencies in our ApiModule.cs.

Additionally, we need to install another package called EntityFrameworkCore.Design. This package, provided by Microsoft, is required for running migrations.

Let’s take care of that now.

dotnet add package Microsoft.EntityFrameworkCore.Design --version 9.0.1
[DependsOn(typeof(AbpEntityFrameworkCoreModule))]
[DependsOn(typeof(AbpEntityFrameworkCorePostgreSqlModule))]

Next, let’s create a simple entity named Todo.cs inside the Entities folder.

using Volo.Abp.Domain.Entities;

namespace Simple.TodoApp.Entities;
public class Todo: AggregateRoot<Guid>
{
    public string Title {get; set;}
    public string Content {get; set;}

    public bool IsCompleted {get; set;}
}

Next, create the database context by inheriting from the AbpDbContext. To do this, create a new file named TodoAppDbContext.cs inside the Data folder.

using Microsoft.EntityFrameworkCore;
using Simple.TodoApp.Entities;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.Modeling;

namespace Simple.TodoApp.Data;

public class TodoAppDbContext(DbContextOptions<TodoAppDbContext> options): AbpDbContext<TodoAppDbContext>(options)
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Todo>(b => {
            b.ToTable("Todos");
            b.ConfigureByConvention();

            b.Property(x => x.Title).IsRequired().HasMaxLength(120);
            b.Property(x => x.Content).HasMaxLength(1000);
            b.Property(x => x.IsCompleted).HasDefaultValue(false);
        });
    }

    public DbSet<Todo> Todos {get; set;}
}

In the code above, we are overriding the OnModelCreating method to configure the entity mappings. It’s important to call the ConfigureByConvention() method, as it ensures that the base properties are properly configured.

Optional: You can also use data annotation attributes for validation if desired.

Next, we need to update the ApiModule.cs file to configure the database. Since our ApiModule class already inherits from the AbpModule base class, we can override the configuration as shown below:

    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        context.Services.AddAbpDbContext<TodoAppDbContext>(options => {
            options.AddDefaultRepositories(includeAllEntities: true);
        });

        Configure<AbpDbContextOptions>(options => {
            options.UseNpgsql();
        });
    }

This step essentially registers the TodoAppDbContext with the dependency injection container. By invoking the AddDefaultRepositories method, ABP automatically creates default repositories for the entities defined in our TodoAppDbContext.

Before running the dotnet-ef migrations, we need to configure the connection strings. To do this, update the appsettings.Development.json file with the following content:

  "ConnectionStrings": {
    "Default": "Host=localhost;Port=5432;Database=TodoAppDemo;Username=postgres;Password=password;"
  }

Optional: You can replace the connection string with your own PostgreSQL connection string.

Now, it’s time to run the initial migrations.

 dotnet ef  migrations add  Initial -o ./Migrations

Don’t forget to install dotnet-ef tool if you haven’t installed it on your machine. Run this command: dotnet tool install --global dotnet-ef

If the migration is successful, you should see the generated migration files in the Migrations folder. Review the files to ensure everything looks correct. If everything checks out, proceed to update the database by running the update-database command.

dotnet ef database update

We’re almost at the end of this blog yes, you read that right! Now, it’s time to let ABP work its magic.

Step 3: Enable Automatic Controllers

To get started, create a file named TodosService.cs inside the Service folder. This file will handle the service logic for the Todo entity.

using Simple.TodoApp.Entities;
using Volo.Abp.Application.Servis;
using Volo.Abp.Domain.Repositories;

namespace Simple.TodoApp.Services;

public class TodosService(IRepository<Todo, Guid> todos) : ApplicationService
{
    public async Task<List<Todo>> GetAll()
    {
        return await todos.GetListAsync();
    }

    public async Task<Todo> Get(Guid id)
    {
        return await todos.GetAsync(id);
    }

    public async Task<Todo> Create(Todo todo)
    {
        return await todos.InsertAsync(todo);
    }

    public async Task<Todo> Update(Todo todo)
    {
        await todos.GetAsync(todo.Id);
        return await todos.UpdateAsync(todo);
    }

    public async Task Delete(Guid id)
    {
        await todos.DeleteAsync(id);
    }
}

The key concept here is the ApplicationService class. When you create an application service, you typically want to expose it as a REST API controller. This is where ABP shines. It can automatically configure your application services as API controllers by convention.

You can also customize this behavior to suit your needs. If you’re interested in learning more, be sure to check out the ABP documentation for additional details and customization options.

To make the setup work, we need to configure a few additional settings in our ApiModule.cs file.

// Update module dependecies 
[DependsOn(typeof(AbpAspNetCoreMvcModule))]
[DependsOn(typeof(AbpSwashbuckleModule))]

public class ApiModule : AbpModule
{
    public override void PreConfigureServices(ServiceConfigurationContext context)
    {
        PreConfigure<AbpAspNetCoreMvcOptions>(options =>
        {
            options.ConventionalControllers.Create(typeof(ApiModule).Assembly, opt =>
            {
                opt.RootPath = "services";
            });
        });
    }

    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        context.Services.AddControllers();

        context.Services.AddAbpSwaggerGen(options =>
        {
            options.SwaggerDoc("v1", new OpenApiInfo{Title = "Simple Todo Api using ABP Modules", Version = "v1"});
            options.DocInclusionPredicate((docName, description) => true);
            options.CustomSchemaIds(x => x.FullName);
            options.HideAbpEndpoints();
        });
    }

    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        app.UseSwagger();
        app.UseAbpSwaggerUI(opt =>
        {
            opt.SwaggerEndpoint("/swagger/v1/swagger.json", "Todo Api using ABP Modules V1");
        });
    }
}

We need to override the PreConfigureServices method in our ApiModule.cs file to configure conventional controllers. This method scans all the assemblies within our module and allows us to define root paths, overriding the default path values if needed.

Why Swagger?

Including Swagger is optional, but I’ve added it to this post because it makes API testing and documentation much easier. With the AbpSwashbuckleModule, integrating Swagger into your API is seamless.

Now, click the Run button or type dotnet run in the terminal. Open your browser and navigate to http://localhost:5222/swagger to see the Swagger UI in action.

Build API using ABP modules.

Our final ApiModule.cs file should now look like this:

using Microsoft.OpenApi.Models;
using Simple.TodoApp.Data;
using Volo.Abp;
using Volo.Abp.AspNetCore;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Autofac;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.PostgreSql;
using Volo.Abp.Modularity;
using Volo.Abp.Swashbuckle;

namespace Simple.TodoApp;

[DependsOn(typeof(AbpAspNetCoreModule))]
[DependsOn(typeof(AbpAspNetCoreMvcModule))]
[DependsOn(typeof(AbpAutofacModule))]
[DependsOn(typeof(AbpEntityFrameworkCoreModule))]
[DependsOn(typeof(AbpEntityFrameworkCorePostgreSqlModule))]
[DependsOn(typeof(AbpSwashbuckleModule))]
public class ApiModule : AbpModule
{
    public override void PreConfigureServices(ServiceConfigurationContext context)
    {
        PreConfigure<AbpAspNetCoreMvcOptions>(options =>
        {
            options.ConventionalControllers.Create(typeof(ApiModule).Assembly, opt =>
            {
                opt.RootPath = "services";
            });
        });
    }

    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        context.Services.AddControllers();

        context.Services.AddAbpSwaggerGen(options =>
        {
            options.SwaggerDoc("v1", new OpenApiInfo{Title = "Simple Todo Api using ABP Modules", Version = "v1"});
            options.DocInclusionPredicate((docName, description) => true);
            options.CustomSchemaIds(x => x.FullName);
            options.HideAbpEndpoints();
        });
        
        context.Services.AddAbpDbContext<TodoAppDbContext>(options => {
            options.AddDefaultRepositories(includeAllEntities: true);
        });

        Configure<AbpDbContextOptions>(options => {
            options.UseNpgsql();
        });
    }

    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var app = context.GetApplicationBuilder();
        var env = context.GetEnvironment();
        if(env.IsDevelopment()) {
            app.UseHsts();
        } else {
            app.UseHttpsRedirection();
        }

        app.UseRouting();
        app.UseSwagger();
        app.UseAbpSwaggerUI(opt =>
        {
            opt.SwaggerEndpoint("/swagger/v1/swagger.json", "Todo Api using ABP Modules V1");
        });
        app.UseConfiguredEndpoints(opt => {
            opt.MapControllers();
            opt.MapOpenApi();
        });
    }
}

Congratulations! You now have a fully functional Web API project powered by ABP Modules. The ABP Framework is an excellent choice for building modern applications as it adheres to best practices and provides a comprehensive set of features out of the box.

If you’re planning to build an enterprise-grade product with all the essential tools and features included, be sure to explore ABP Framework.

If you found this guide helpful and want to learn more about building modern, scalable, and efficient applications, make sure to follow me! I regularly share tips, tutorials, and deep dives into frameworks like ABP, ASP.NET Core, and more.

Subscribing to my blog will keep you updated with the latest content, practical advice, and tools to elevate your development skills. Don’t miss out on staying ahead in the ever-evolving world of software development hit that follow button and subscribe now! Let’s build amazing things together. 🚀

Reply

or to participate.