Miscellaneous Customizations of the Audit Trail System (XPO)
- 3 minutes to read
Implement Custom Persistent Object to be Used as the Audit Data Storage
The Audit Trail Module uses the AuditDataItemPersistent class as the audit data storage. To store extra audit information, inherit from this class or implement the IAuditDataItemPersistent interface. In the overridden OnSaving method of your AuditDataItemPersistent descendant, initialize extra properties based on the AuditedObject, PropertyName, and other information from the base class. Set the AuditDataItemPersistentType property to your class to use it instead of the default class.
Specify the String Representation of the Null Value
The Audit Trail Module saves the null value as the “N/A” string. To change its string representation, navigate to the Localization | AuditTrail node in the Model Editor and specify the NullValueString property.

If you want to use a different null value representation in audit records in the UI, set AuditTrailServiceRoot.AuditDataStore‘s NullValueString property as shown below.
File: MySolution.Blazor.Server\Startup.cs, MySolution.Win\Startup.cs, MySolution.WebApi\Startup.cs
using DevExpress.Persistent.AuditTrail;
// ...
builder.Modules
.AddAuditTrailXpo(o => {
o.Events.OnCustomizeAuditDataStore = context => {
context.AuditDataStore.NullValueString = "";
};
})
Customize the Blob Properties Storage Mechanism
The Audit Trail Module saves Blob property values as the “Blob data” string. Follow the steps below to customize this behavior.
Note
You can also use this technique to specify the string representation of reference type properties.
Implement the
AuditDataStoredescendant and override itsGetDefaultStringRepresentationmethod.File: MySolution.Module\CustomAuditDataStore.cs.
using DevExpress.Persistent.AuditTrail; using DevExpress.Persistent.BaseImpl; // ... public class CustomAuditDataStore : AuditDataStore<AuditDataItemPersistent, AuditedObjectWeakReference> { protected override string GetDefaultStringRepresentation(object value) { string result = base.GetDefaultStringRepresentation(value); if(result == BlobDataString) return GetCustomBlobDataString(value); return result; } private string GetCustomBlobDataString(object value) { // ... } }Configure the Audit Trail system to use your custom data store. In the application’s Startup.cs file, add the following code to the
AddAuditTrailXpomethod call.File: MySolution.Blazor.Server\Startup.cs, MySolution.Win\Startup.cs, MySolution.WebApi\Startup.cs
using DevExpress.Persistent.AuditTrail; // ... builder.Modules .AddAuditTrailXpo(o => { o.Events.OnCustomCreateAuditDataStore = context => { context.AuditDataStore = new CustomAuditDataStore(); }; })
Implement a Custom AuditTrailService
In XAF applications, the Audit Trail system is implemented as a service (IAuditTrailService). You can create and register your own implementation of the IAuditTrailService as this section describes.
To create a custom IAuditTrailService, you can use the AuditTrailServiceBase type as a base type for your custom implementation or implement the IAuditTrailService interface from scratch:
using DevExpress.Persistent.BaseImpl.AuditTrail.Services;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
public class CustomAuditTrailService : AuditTrailServiceBase {
private const string unknownUserName = "Unknown";
private readonly ISecurityStrategyBase securityStrategy;
private readonly IAuditTrailServiceRoot auditTrailServiceRoot;
private bool isSetupAuditCalled = false;
public CustomAuditTrailService(IServiceProvider serviceProvider, IAuditTrailServiceRoot auditTrailServiceRoot, IOptionsSnapshot<AuditTrailOptions> auditTrailServiceOptions) :
base(serviceProvider, auditTrailServiceRoot, auditTrailServiceOptions.Value) {
this.securityStrategy = serviceProvider.GetService<ISecurityStrategyBase>();
this.auditTrailServiceRoot = auditTrailServiceRoot;
}
protected override void EnsureSetupAudit() {
if(!isSetupAuditCalled) {
isSetupAuditCalled = true;
auditTrailServiceRoot.SetupAudit(serviceProvider.GetRequiredService<ITypesInfo>(), Options.AuditDataItemPersistentType);
}
}
protected override string GetCurrentUserNameCore() {
return securityStrategy != null ? securityStrategy.UserName : unknownUserName;
}
}
Register your custom IAuditTrailService type as a scoped service:
File: MySolution.Blazor.Server\Startup.cs, MySolution.Win\Startup.cs, MySolution.WebApi\Startup.cs
// ...
public class Startup {
// ...
public void ConfigureServices(IServiceCollection services) {
// ...
services.AddXaf(Configuration, builder => {
builder.UseApplication<MyBlazorApplication>();
builder.Modules
// ...
.AddAuditTrailXpo()
// ...
});
services.AddScoped<IAuditTrailService, CustomAuditTrailService>();
// ...
}
//...
}