Adding a Module to an ABP project made simple
As you can read in the ABP Framework documentation about Modularity, the ABP Framework was designed to support to build fully modular applications.
Today I will show you how to create and integrate your own module, in this case a simple PdfGenerator, into an ABP Framework application.
Source code
The source code of both projects is available on GitHub
Requirements
The following tools are needed to be able to run the solution and follow along.
- .NET 8.0 SDK
- VsCode, Visual Studio 2022 or another compatible IDE
- ABP CLI 8.0.0
Create a new ABP Framework application
abp new BookStore -u blazor -o BookStore
- Run the DbMigrator project to apply the database migrations
- Start both the HttpApi.Host project and Blazor project
- Stop both the HttpApi.Host project and Blazor project
Create a PdfGenerator ABP Module
Add a modules folder to the root of the project
Open a command prompt in the modules folder of the project and add a new class library
dotnet new classlib -n PdfGenerator
Add PdfGenerator project to solution
Go to the root of your ABP project and run the command below:
dotnet sln add modules\PdfGenerator\PdfGenerator.csproj
Install Nuget packages
Open a command prompt in the root of the PdfGenerator class library and install the Nuget packages below.
abp add-package Volo.Abp.Core
dotnet add package PdfSharpCore
Add a PdfGeneratorSettings section to the appsettings.json file in hte HttpApi.Host project
"PdfGeneratorSettings": {
"UserName": "your-username",
"EmailAddress": "your-username@hotmail.com"
}
Add a PdfGeneratorSettingsOptions class to the PdfGenerator class library project
namespace PdfGenerator
{
public class PdfGeneratorSettingsOptions
{
public string? UserName { get; set; }
public string? EmailAddress { get; set; }
}
}
Add a MyPdfGeneratorModule class to the PdfGenerator class library project
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
namespace PdfGenerator
{
public class MyPdfGeneratorModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<PdfGeneratorSettingsOptions>(configuration.GetSection("PdfGeneratorSettings"));
context.Services.AddTransient<IPdfGeneratorService, PdfGeneratorService>();
}
}
}
Add an IPdfGeneratorService interface and a PdfGeneratorService class to the PdfGenerator class library project
using Microsoft.Extensions.Options;
using PdfSharpCore.Drawing;
using PdfSharpCore.Fonts;
using PdfSharpCore.Pdf;
using PdfSharpCore.Utils;
namespace PdfGenerator
{
public interface IPdfGeneratorService
{
Task<byte[]> Generate();
}
public class PdfGeneratorService(IOptions<PdfGeneratorSettingsOptions> options) : IPdfGeneratorService
{
private readonly PdfGeneratorSettingsOptions _options = options.Value;
public async Task<byte[]> Generate()
{
if (GlobalFontSettings.FontResolver is not FontResolver)
GlobalFontSettings.FontResolver = new FontResolver();
var document = new PdfDocument();
var page = document.AddPage();
var gfx = XGraphics.FromPdfPage(page);
var font = new XFont("Arial", 20, XFontStyle.Bold);
var textColor = XBrushes.Black;
var layout = new XRect(20, 20, page.Width, page.Height);
var format = XStringFormats.Center;
gfx.DrawString($"Pdf created by {_options.UserName}!", font, textColor, layout, format);
byte[] fileContents;
using (var stream = new MemoryStream())
{
document.Save(stream, true);
fileContents = stream.ToArray();
}
return await Task.FromResult(fileContents);
}
}
}
Add a project reference to the PdfGeneratorModule class library project in the Application project
Open a command prompt in the root of the Application project
dotnet add reference ../../modules/PdfGenerator/PdfGenerator.csproj
Attribute DependsOn in the BookStoreApplicationModule class in the Application project
Add a typeof(MyPdfGeneratorModule) entry in the DependsOnAttribute
[DependsOn(
typeof(BookStoreDomainModule),
typeof(AbpAccountApplicationModule),
typeof(BookStoreApplicationContractsModule),
typeof(AbpIdentityApplicationModule),
typeof(AbpPermissionManagementApplicationModule),
typeof(AbpTenantManagementApplicationModule),
typeof(AbpFeatureManagementApplicationModule),
typeof(AbpSettingManagementApplicationModule),
typeof(MyPdfGeneratorModule)
)]
Add A IExportPdfAppService interface the Application.Contracts project
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
namespace BookStore.Application.Contracts
{
public interface IExportPdfAppService : IApplicationService
{
Task<byte[]> GeneratePdf();
}
}
Add A ExportPdfAppService class to test the Application project
using System.Threading.Tasks;
@using BookStore.Application.Contracts;
using PdfGenerator;
using Volo.Abp.Application.Services;
namespace BookStore
{
public class ExportPdfAppService(IPdfGeneratorService pdfGeneratorService) : ApplicationService, IExportPdfAppService
{
private readonly IPdfGeneratorService _pdfService = pdfGeneratorService;
public async Task<byte[]> GeneratePdf() => await _pdfService.Generate();
}
}
add a exporttopdf.js file to the wwwroot/js folder of the Blazor project
function saveAsFile(filename, bytesBase64) {
var link = document.createElement('a');
link.download = filename;
link.href = 'data:application/octet-stream;base64,' + bytesBase64;
document.body.appendChild(link); // Needed for Firefox
link.click();
document.body.removeChild(link);
}
add a reference to exporttopdf.js in the index.html file in the Blazor project
<!DOCTYPE html>
<html>
<!-- other code here ... -->
<script src="js/exporttopdf.js"></script>
</body>
</html>
Replace contents from Index.razor page in the Blazor project
@page "/"
@using BookStore.Application.Contracts;
@using Microsoft.JSInterop
@inject IExportPdfAppService ExportPdfAppService
@inject IJSRuntime JsRuntime
<Row Class="d-flex px-0 mx-0 mb-1">
<Button Clicked="@(ExportPdf)" class="p-0 ml-auto mr-2" style="background-color: transparent"
title="Download">
<span class="fa fa-file-pdf fa-lg m-0" style="color: #008000; background-color: white;"
aria-hidden="true">
</span>
Generate Pdf!
</Button>
</Row>
@code {
private async Task ExportPdf()
{
var pdfBytes = await ExportPdfAppService.GeneratePdf();
await JsRuntime.InvokeVoidAsync("saveAsFile", $"test_{DateTime.Now.ToString("yyyyMMdd_HHmmss")}.pdf",
Convert.ToBase64String(pdfBytes));
}
}
Test the PdfGenerator module
Start both the Blazor and the HttpApi.Host project to run the application
and test out the PdfGenerator module you just created.
Get the source code on GitHub.
Enjoy and have fun!
Comments
JS c 39 weeks ago
I need technical and vendor contact information
Sajankumar Vijayan 39 weeks ago
Nice one.
Mark0Carlson 15 weeks ago
Yeah it can be a real headache, I tried LongPath Tool Program which helped a lot.