Open Closed

Audit Logging fails ONLY for Create/Update in Blazor Server (Direct Injection) #10198


User avatar
0
nskelleci created

Hi ABP Team,

I am experiencing a weird issue with Audit Logging in my Blazor Web App (InteractiveServer) project (ABP v10).

My Setup: I am injecting my AppServices directly into my Razor pages (I am NOT using HttpClient/RestService).

The Problem:

DeleteAsync: Works perfectly. The audit log is created, and I can see the entity changes.

GetListAsync: Works perfectly. The audit log is created.

CreateAsync / UpdateAsync: NOT WORKING. The data is saved to the database successfully, but NO Audit Log is created at all.

My Findings:

If I call the exactly same AppService methods via Swagger/API, Create and Update logs are working fine.

The issue only happens when calling the service directly from a Blazor Page.

Since DeleteAsync (which takes a simple Guid) works, but Create/Update (which take DTOs) fails silently, I suspect there is an issue with serialization or interception context in Blazor Server mode.

Is there a known configuration or workaround for this?

Thanks.

  • Template: app
  • Created ABP Studio Version: 2.1.0
  • Current ABP Studio Version: 2.1.0
  • Tiered: No
  • Multi-Tenancy: Yes
  • UI Framework: blazor-webapp
  • Theme: leptonx
  • Theme Style: system
  • Theme Menu Placement: side
  • Run Install Libs: No
  • Database Provider: ef
  • Database Management System: sqlserver
  • Separate Tenant Schema: No
  • Mobile Framework: none
  • Public Website: No
  • Social Login: No
  • Include Tests: Yes
  • Kubernetes Configuration: No
  • Distributed Event Bus: none
  • Use Local References: No
  • Optional Modules:
    • GDPR
    • LanguageManagement
    • AuditLogging
    • OpenIddictAdmin
  • Selected Languages: English, Arabic, Chinese (Simplified), Chinese (Traditional), Czech, English (United Kingdom), Finnish, French, German (Germany), Hindi , Hungarian, Icelandic, Italian, Portuguese (Brazil), Romanian (Romania), Russian, Slovak, Spanish, Swedish, Turkish
  • Default Language: English
  • Create Command: abp new NebulaCLMS_V5 -t app --ui-framework blazor-webapp --database-provider ef --database-management-system sqlserver --theme leptonx --skip-migration --skip-migrator --without-cms-kit --dont-run-install-libs --dont-run-bundling --no-social-logins -no-file-management -no-text-template-management
  • Exception message and full stack trace: No exception
  • Steps to reproduce the issue:

7 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    1. Solution The behavior you see is expected when you directly inject and call your Application Services from Blazor (server) components/pages. ABP’s method-level auditing and parameter serialization are applied by dynamic proxies (Castle interceptors). When you resolve and call a class directly (not via its proxied interface), or you call it from a scope that suppresses the auditing pipeline, the interceptor doesn’t run, so:
    • Get/List (GET) may still appear in logs depending on AbpAuditingOptions (e.g., IsEnabledForGetRequests=true);
    • Delete(Guid) may be logged because the service was resolved via its interface or another code path with a proxy;
    • Create/Update with DTO parameters won’t be logged if the proxy is bypassed (no parameter serialization, no audit action, no entity change capture).

    Fixes (apply all that match your case):

    • Always depend on and inject the interface, not the concrete class. The proxy is generated on the interface service. Example:
    public partial class MyPage : ComponentBase
      {
          // Good: inject the interface that inherits IApplicationService
          [Inject] private IMyEntityAppService MyEntityAppService { get; set; } = default!;
    
          private async Task SaveAsync(CreateMyEntityDto input)
          {
              await MyEntityAppService.CreateAsync(input); // audited by proxy
          }
      }
    

    Ensure your interface implements IApplicationService:

    public interface IMyEntityAppService : IApplicationService
      {
          Task<MyEntityDto> CreateAsync(CreateMyEntityDto input);
          Task<MyEntityDto> UpdateAsync(Guid id, UpdateMyEntityDto input);
          Task DeleteAsync(Guid id);
          Task<PagedResultDto<MyEntityDto>> GetListAsync(PagedAndSortedResultRequestDto input);
      }
    
    • Do not inject the concrete class into the Blazor page/component. If you must access implementation-only members, expose them via the interface or use a domain service behind the app service.

    • Ensure auditing is enabled for app service methods:

      • AbpAuditingOptions.IsEnabled = true (default).
      • If you want GETs too, set IsEnabledForGetRequests = true.
      • Do not decorate Create/Update with [DisableAuditing].
      • If you have custom contributors or stores, keep options.HideErrors true so audit pipeline continues on failures. Example:
    Configure<AbpAuditingOptions>(options =>
      {
          options.IsEnabled = true;
          options.IsEnabledForGetRequests = true; // optional
          options.AlwaysLogOnException = true;
          // Track entity changes if needed:
          options.EntityHistorySelectors.AddAllEntities();
      });
    
    • Use app.UseAuditing() in the host startup (so request-level data is collected). In ABP module:
    public override void OnApplicationInitialization(ApplicationInitializationContext context)
      {
          var app = context.GetApplicationBuilder();
          app.UseAuditing();
      }
    
    • Ensure the UoW is active and SaveChanges happens inside the same call so EntityChange logs are produced. Using standard repositories inside application services with ABP’s UnitOfWork ensures that. Avoid calling repositories outside of an app-service/UoW boundary.

    • Confirm you are not suppressing interceptors:

      • Don’t resolve services from a created instance via ActivatorUtilities or newing up the class; always resolve via DI.
      • Don’t mark the app service as non-virtual; ABP’s dynamic proxy requires virtual methods. App service methods should be virtual (default in templates).
      • Keep the class public.
    • If you have replaced IAuditingStore or use a remote store, ensure SaveAsync is called; check logs of your auditing store.

    • Quick verification

      • Put a breakpoint/log inside your app service CreateAsync/UpdateAsync and verify it’s called via proxy: you should see serialized parameters in the audit log later and method is virtual.
      • Temporarily add [Audited] on the app service class or methods to force-enable:
    [Audited]
        public class MyEntityAppService : ApplicationService, IMyEntityAppService
        {
            public virtual async Task<MyEntityDto> CreateAsync(CreateMyEntityDto input) { ... }
        }
    

    If you instead prefer calling over HTTP even from Blazor Server, you can inject the dynamic C# client (HttpApi.Client) for the same interface. That path always goes through the server pipeline and auditing.

    1. Sources
    • ABP Auditing options (source code reference for options type):
      • https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingOptions.cs

    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

  • User Avatar
    0
    nskelleci created

    I always inject interface of services as below; [Inject] protected ISeasonAppService _seasonAppService { get; set; } = default!;

    this is my service interface inherited from ICrudAppService; public interface ISeasonAppService : ICrudAppService<SeasonDto, Guid, PagedAndSortedResultRequestDto, CreateSeasonDto, UpdateSeasonDto> { }

    I think I am blind now to not see what I am doing wrong.

  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    1. Solution Given you already inject the interface (ICrudAppService-based), the most common reasons Create/Update are not audited when called directly from Blazor Server are:
    • The call bypasses ABP’s interception pipeline. Checklist:

      • Ensure the concrete AppService methods are public and virtual (Castle proxy needs virtual).
      • Do not new-up the service or resolve it via ActivatorUtilities. Always resolve from DI and via the interface (you already do).
      • Make sure there is no decorator/wrapper that calls the implementation directly (e.g., a factory returning the concrete type).
    • Auditing middleware and options

      • Confirm app.UseAuditing() is added in the host OnApplicationInitialization:
    public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            var app = context.GetApplicationBuilder();
            app.UseAuditing();
        }
    
    • Keep auditing enabled and do not disable for your methods:
    Configure<AbpAuditingOptions>(options =>
        {
            options.IsEnabled = true;
            // optional:
            // options.IsEnabledForGetRequests = true;
            // options.AlwaysLogOnException = true;
        });
    
    • Ensure you didn’t use [DisableAuditing] on the service or method.

    • Entity change logs require UoW + change tracking selection Create/Update “action logs” should appear even without entity changes, but if you expect EntityChanges, ensure:

      • You execute repository operations inside the app service call (ABP opens a UoW for app services).
      • You enabled entity change selectors (or used [Audited] on entities you want tracked):
    Configure<AbpAuditingOptions>(options =>
        {
            options.EntityHistorySelectors.AddAllEntities(); // or add your entity types explicitly
        });
    
    • Your DbContext is not being saved outside ABP’s UoW unexpectedly.

    • Verify the proxy is actually used in Blazor Temporarily assert interception by forcing auditing:

    [Audited]
      public class SeasonAppService : CrudAppService<Season, SeasonDto, Guid, PagedAndSortedResultRequestDto, CreateSeasonDto, UpdateSeasonDto>,
                                      ISeasonAppService
      {
          public override async Task<SeasonDto> CreateAsync(CreateSeasonDto input) { ... }
          public override async Task<SeasonDto> UpdateAsync(Guid id, UpdateSeasonDto input) { ... }
      }
    

    If this still doesn’t create an audit log, interception is likely bypassed.

    • Method signature and virtual override Since you inherit from CrudAppService:
      • Override methods as public virtual and call base if needed:
    public override async Task<SeasonDto> CreateAsync(CreateSeasonDto input)
        {
            return await base.CreateAsync(input);
        }
    
        public override async Task<SeasonDto> UpdateAsync(Guid id, UpdateSeasonDto input)
        {
            return await base.UpdateAsync(id, input);
        }
    

    Do not seal the class or methods.

    • Avoid resolving the implementation behind the interface inside the same scope In Blazor components, keep only the interface injected:
    [Inject] protected ISeasonAppService SeasonAppService { get; set; } = default!;
    

    Do not cast to the concrete type or call non-virtual helpers on the class.

    • Custom IAuditingStore If you replaced IAuditingStore, ensure SaveAsync is called and not filtering out Create/Update. Log inside your store to verify it’s invoked for Create/Update paths.

    • Quick diagnostic

      • Add a simple [Audited] method with a primitive parameter on the same service and call it from the page. If no audit entry appears, the interceptor is not running for Blazor calls.
      • Log typeof(ISeasonAppService).FullName from the injected instance’s runtime type. If it’s Castle.Proxies.* it’s proxied; if it’s your concrete type, interception is bypassed.

    If after all checks Delete is audited but Create/Update are not:

    • It’s often because Create/Update are not virtual (or overridden) in your derived service, or a [DisableAuditing] attribute is present via a base class/assembly-level. Make Create/Update public virtual in your SeasonAppService (even if they just call base) and try again.
    1. Sources
    • AbpAuditingOptions (source) (validated):
      • https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingOptions.cs

    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

  • User Avatar
    0
    nskelleci created

    Hi,

    I have tried all the suggestions mentioned above, but the issue persists.

    What I have verified:

    Proxy is Active: I logged the runtime type of the injected service in my Blazor component. It is definitely proxied: Castle.Proxies.IApplicationServiceProxy.... The interception pipeline IS running.

    Overrides Added: I explicitly overrode CreateAsync and UpdateAsync methods in my concrete SeasonAppService class to ensure they are visible to the proxy mechanism, calling base.CreateAsync(input).

    Attributes: I added [Audited] attribute to the AppService class.

    Configuration: EntityHistorySelectors.AddAllEntities() is enabled.

    Current Behavior:

    DeleteAsync(Guid id): Works perfectly. Audit log creates, Entity Change records are inserted.

    CreateAsync(CreateDto input) / UpdateAsync(...): Still NO Audit Log. Not even an "Action" log. It fails silently regarding auditing, but the database record is updated successfully.

  • User Avatar
    0
    nskelleci created

    https://github.com/nskelleci/audittest

    You can see in this repo. I have created layered project by using Abp Studio.

    created Product entity created Product DTOs created IProductAppService inherited from ICrudAppService created ProductAppService inherited from CrudAppService created Mappings using Mapperly (AI assisted code) Audit configuration added to BlazorModule

    Configure<AbpAuditingOptions>(options =>
            {
                options.IsEnabled = true;
                options.EntityHistorySelectors.AddAllEntities();
            });
    

    created Migration applied Migration to DB

    Ran Blazor App goto /Products Page Added new product updated the product

    Unfourtanetly no audit logs at all..

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Please make your repo PRIVATE

    I will check it.

    Thanks,.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    In short, the audit logging feature in Blazor Server will not work properly.

    Blazor Server uses SignalR for communication instead of separate HTTP requests.

    It doesn't have any mechanism for us to intercept method calls and log them.

    https://github.com/dotnet/aspnetcore/issues/30115

    Sorry for that.

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 v10.1.0-preview. Updated on December 10, 2025, 12:02
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.