Search code examples
aspnetboilerplate

ASPNET Boilerplate, extending audit log


I am trying to extend the AuditLog entity in ASPNETBOILETPLATE framework in order to add some new properties to it. I have tried to extend the AuditLog class (ExtendedAuditInfo) and implement a customised version of AuditStore Class (ExtendedAuditStore). However, I am not able to inject my new ExtendedAuditInfo in the constructor and receive two error messages regarding unmatching input parameters in the Constructor and SaveAsync method.

Class ExtendedAuditInfo:

public class ExtendedAuditInfo : AuditInfo
{
    // Some properties
}

Class ExtendedAuditStore:

public class ExtendedAuditStore : AuditingStore
{
    public ExtendedAuditStore(IRepository<ExtendedAuditInfo, long> auditLogRepository)
        : base(auditLogRepository)
    {
    }

    public override Task SaveAsync(ExtendedAuditInfo auditInfo)
    {
        if (!string.IsNullOrEmpty(auditInfo.Parameters) && auditInfo.Parameters != "{}")
        {
            var parameters = JsonConvert.DeserializeObject<AuditParameterInput>(auditInfo.Parameters);
            if (parameters != null)
                auditInfo.CustomData = parameters.Input.Id.ToString();
        }

        return base.SaveAsync(auditInfo);
    }
}

The errors are:

cannot convert from 'Abp.Domain.Repositories.IRepository<SixB.Serafina.Auditing.ExtendedAuditInfo, long>' to 'Abp.Domain.Repositories.IRepository<Abp.Auditing.AuditLog, long>'

and

no suitable method found to override

The procedure above is based on the idea that I found Here


Solution

  • I found the solution based on the official document of How To Extend Existing Entities.

    In order to extend the AuditLog class, inheritance must be used. Therefore a new class, let's say ExtendedAuditInfo needs to be inherited from AuditLog.

    public class ExtendedAuditLog : AuditLog
        {
            public ExtendedAuditLog()
            {
    
            }
    
            public ExtendedAuditLog(AuditInfo auditInfo)
            {
                this.BrowserInfo = auditInfo.BrowserInfo;
                this.ClientIpAddress = auditInfo.ClientIpAddress;
                this.ClientName = auditInfo.ClientName;
                this.CustomData = auditInfo.CustomData;
                this.Exception = auditInfo.Exception?.Message.ToString() + "";
                this.ExecutionDuration = auditInfo.ExecutionDuration;
                this.ExecutionTime = auditInfo.ExecutionTime;
                this.ImpersonatorTenantId = auditInfo.ImpersonatorTenantId;
                this.ImpersonatorUserId = auditInfo.ImpersonatorUserId;
                this.MethodName = auditInfo.MethodName;
                this.Parameters = auditInfo.Parameters;
                this.ReturnValue = auditInfo.ReturnValue;
                this.ServiceName = auditInfo.ServiceName;
                this.TenantId = auditInfo.TenantId;
                this.UserId = auditInfo.UserId;
            }
    
            //new properties
        }
    

    This class has to be added to the context and obviously, a new migration needs to be run in order to add the new properties.

    public class ProjectDbContext : AbpZeroDbContext<Tenant, Role, User, ProjectDbContext >
    {
        /* Define a DbSet for each entity of the application */
        
        public SerafinaDbContext(DbContextOptions<SerafinaDbContext> options)
            : base(options)
        {
        }
    
        public virtual DbSet<County> Counties { get; set; }
    
        public virtual DbSet<Country> Countries { get; set; }
    
        public virtual DbSet<Currency> Currencies { get; set; }
    
        public virtual DbSet<OrganisationType> OrganisationTypes { get; set; }
    
        public virtual DbSet<ExtendedAuditLog> ExtendedAuditLogs { get; set; }
    }
    

    Finally, in the ExtendedAuditStore class, IRepository<ExtendedAuditLog, long> _extendedAuditLogRepository has to be injected as a second parameter of the constructor and can be used to insert the extended entity.

    public class ExtendedAuditStore : AuditingStore
    {
        IRepository<ExtendedAuditLog, long> _extendedAuditLogRepository;
    
        public ExtendedAuditStore(
            IRepository<AuditLog, long> auditLogRepository,
            IRepository<ExtendedAuditLog, long> extendedAuditLogRepository
            )
            : base(auditLogRepository)
        {
            _extendedAuditLogRepository = extendedAuditLogRepository;
        }
    
        public override async Task SaveAsync(AuditInfo auditInfo)
        {
            if (auditInfo.Exception != null)
                await base.SaveAsync(auditInfo);
    
            var auditLog = new ExtendedAuditLog(auditInfo);
            //new properties can be set here
            await _extendedAuditLogRepository.InsertAsync(auditLog);
        }
    }
    

    Also, instead of inheriting from AuditingStore, a new implementation for IAuditingStore can be created and injected into application services.

    UPDATE:

    Finally, all you need is to replace the default AuditingStore in StartUp class:

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddTransient<IAuditingStore, ExtendedAuditStore>();
    }