Open Closed

Accessing Audit Logs of Entity Changes in a Custom Frontend Screen — Need Guidance and Confirmation #9419


User avatar
0
alva.gabriel created

Hi ABP Team

We have a scenario where we need to present the audit trail of an entity’s changes on a custom frontend screen. We were reviewing the official ABP documentation but couldn’t find a specific or recommended approach to access this audit data from the backend in a way that suits our needs.

We found a third party forum post mentioning the use of EfCoreAuditLogRepository for a similar use case. Based on that hint, we installed the Volo.Abp.AuditLogging.EntityFrameworkCore package in the Application layer of our ABP module and explicitly instantiated it in the ProcurementApplicationModule class. With this setup, we were able to retrieve the entity changes as needed for our frontend.

We would like to clarify two things:

1. Is there any official or recommended approach by ABP to access an entity’s audit logs according to our use case? If so, we would really appreciate it if you could share a link to the official documentation or any related resources.

2. If ABP does not have an official or recommended approach for this scenario, would our current implementation (using EfCoreAuditLogRepository in the Application layer) cause any issues now or in the future, or is it acceptable to continue working this way?

For your reference, here is a simplified version of our code in the ApplicationModule and AppService classes:

// ApplicationModule
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AutoMapper;
using Volo.Abp.Modularity;
using Volo.Abp.Application;
using Volo.Abp.BackgroundWorkers.Hangfire;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.BackgroundWorkers;
using Aptim.MiApptimWeb.Procurement.Orders;
using Volo.Abp.AuditLogging.EntityFrameworkCore;

namespace Aptim.MiApptimWeb.Procurement;

[DependsOn(
    typeof(ProcurementDomainModule),
    typeof(ProcurementApplicationContractsModule),
    typeof(AbpDddApplicationModule),
    typeof(AbpAutoMapperModule)
    )]
[DependsOn(typeof(AbpBackgroundWorkersHangfireModule))]
public class ProcurementApplicationModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        context.Services.AddAutoMapperObjectMapper<ProcurementApplicationModule>();
        Configure<AbpAutoMapperOptions>(options =>
        {
            options.AddMaps<ProcurementApplicationModule>(validate: true);
        });

        context.Services.AddTransient<EfCoreAuditLogRepository>();
    }

    public override async Task OnApplicationInitializationAsync(
        ApplicationInitializationContext context)
    {
        await context.AddBackgroundWorkerAsync<MyLogWorker>();
    }
}
// AppService
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Volo.Abp.AuditLogging.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories;

namespace Aptim.MiApptimWeb.Procurement.Orders;

public class ProcessedOrderAppService(
    IRepository<ProcessedOrders, Guid> _repository,
    IRepository<StagingPurchaseOrderDetail, Guid> _stagingRepository,
    EfCoreAuditLogRepository _auditLogRepository
) : ProcurementAppService, IProcessedOrderAppService
{
    public async Task<List<EntityChangeDto>> GetProcessedOrderChangesAsync(Guid entityId)
    {
        var entityChanges = await _auditLogRepository.GetEntityChangeListAsync(
            includeDetails: true,
            entityId: entityId.ToString()
        );

        var result = entityChanges
            .OrderBy(ec => ec.ChangeTime)
            .Select(ec => new EntityChangeDto
            {
                ChangeTime = ec.ChangeTime,
                ChangeType = ec.ChangeType.ToString(),
                PropertyChanges = ec.PropertyChanges
                    .Select(pc => new PropertyChangeDto
                    {
                        PropertyName = pc.PropertyName,
                        OriginalValue = DeserializeValue(pc.OriginalValue),
                        NewValue = DeserializeValue(pc.NewValue)
                    })
                    .ToList()
            })
            .ToList();

        return result;
    }

    private string DeserializeValue(string jsonValue)
    {
        if (jsonValue == null)
        {
            return null;
        }

        try
        {
            return JsonSerializer.Deserialize<string>(jsonValue);
        }
        catch
        {
            return jsonValue;
        }
    }
}

Thank you very much in advance for your support and clarifications.

Best regards, Gabriel


1 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    You can inject the IAuditLogRepository to query the audit logs. It's the same as EfCoreAuditLogRepository

    Use IAuditLogRepository will be fine.

    https://github.com/abpframework/abp/blob/dev/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/IAuditLogRepository.cs

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v9.3.0-preview. Updated on June 13, 2025, 11:37