All docs
V21.2
21.2 (EAP/Beta)
21.1
20.2
The page you are viewing does not exist in version 20.2. This link will take you to the root page.
20.1
The page you are viewing does not exist in version 20.1. This link will take you to the root page.
19.2
The page you are viewing does not exist in version 19.2. This link will take you to the root page.
19.1
The page you are viewing does not exist in version 19.1. This link will take you to the root page.
18.2
The page you are viewing does not exist in version 18.2. This link will take you to the root page.
18.1
The page you are viewing does not exist in version 18.1. This link will take you to the root page.
17.2
The page you are viewing does not exist in version 17.2. This link will take you to the root page.
You are viewing help content for pre-release software. This document and the features it describes are subject to change. Switch to the current version.

Audit Trail Module (EF Core)

  • 6 minutes to read

This topic describes how to add the Audit Trail Module to your ASP.NET Core Blazor or WinForms application and explains the Module’s ORM-specific features.

Audited Objects

The Audit Trail Module logs changes in the following objects and properties:

  • Persistent classes registered as DbSet<TEntity> properties in DbContext.
  • Public writable simple and reference properties defined in persistent classes. The Module does not log changes in read-only properties (properties without the set accessor).
  • Public collection properties defined in persistent classes.

Add the Audit Trail Module to Your Application

Follow the steps below to add the Audit Trail Module to your application. If you added this Module when you created an XAF application, the Solution Wizard generates the following code automatically:

  1. Add the DevExpress.ExpressApp.AuditTrail.EFCore NuGet package to the ASP.NET Core Blazor or WinForms application project (MySolution.Blazor.Server or MySolution.Win).
  2. In the application constructor, add the Audit Trail Module to the Modules collection:

    ASP.NET Core Blazor
    File: MySolution.Blazor.Server\BlazorApplication.cs.

    using DevExpress.ExpressApp.AuditTrail.EFCore;
    // ...
    public partial class MySolutionBlazorApplication : BlazorApplication {
        public MySolutionBlazorApplication() {
            // ...
            Modules.Add(new AuditTrailModule());
        }
        // ...
    }
    

    WinForms
    File: MySolution.Win\WinApplication.cs.

    using DevExpress.ExpressApp.AuditTrail.EFCore;
    // ...
    public partial class MySolutionWindowsFormsApplication : WinApplication {
        public MySolutionWindowsFormsApplication() {
            // ...
            Modules.Add(new AuditTrailModule());
        }
        // ...
    }
    

    Alternatively, you can add the Audit Trail Module to the ModuleBase.RequiredModuleTypes collection of the platform-agnostic or ASP.NET Core Blazor/WinForms-specific Module.

  3. In ASP.NET Core Blazor applications, do the following in the Startup.ConfigureServices method:

    • call the AddAuditTrail method to register the required Audit Trail services in IServiceCollection;
    • call the AddAuditedDbContextFactory method to register IXafDbContextFactory with an Audit Trail service factory in the IServiceCollection.

    File: MySolution.Blazor.Server\Startup.cs.

    using DevExpress.Persistent.BaseImpl.EFCore.AuditTrail;
    // ...
    public class Startup {
        // ...
        public void ConfigureServices(IServiceCollection services){
            //...
            services.AddDbContextFactory<MySolutionEFCoreDbContext>((serviceProvider, options) => {
                string connectionString = Configuration.GetConnectionString("ConnectionString");
                options.UseSqlServer(connectionString);
                options.UseLazyLoadingProxies();
                // Uncomment the following line if your application uses the Security System.
                // options.UseSecurity(serviceProvider);
            }, ServiceLifetime.Scoped);
            services.AddAuditTrail().AddAuditedDbContextFactory<MySolutionEFCoreDbContext>();
        }
    }
    
  4. In the application’s CreateDefaultObjectSpaceProvider overridden method, create EFCoreObjectSpaceProvider or SecuredEFCoreObjectSpaceProvider with the IXafDbContextFactory parameter:

    ASP.NET Core Blazor
    File: MySolution.Blazor.Server\BlazorApplication.cs.

    public class MySolutionBlazorApplication : BlazorApplication {
        // ...
        protected override void CreateDefaultObjectSpaceProvider(CreateCustomObjectSpaceProviderEventArgs args) {
            IXafDbContextFactory<MySolutionEFCoreDbContext> dbFactory = 
                ServiceProvider.GetService<IXafDbContextFactory<MySolutionEFCoreDbContext>>();
            EFCoreObjectSpaceProvider efCoreObjectSpaceProvider = new EFCoreObjectSpaceProvider(dbFactory, TypesInfo);
            // or
            // SecuredEFCoreObjectSpaceProvider efCoreObjectSpaceProvider = 
            // new SecuredEFCoreObjectSpaceProvider((ISelectDataSecurityProvider)Security, dbFactory, TypesInfo);
            args.ObjectSpaceProviders.Add(efCoreObjectSpaceProvider);
            // ...
        }
    }
    

    WinForms
    File: MySolution.Win\WinApplication.cs.

    using DevExpress.Persistent.BaseImpl.EFCore.AuditTrail;
    //...
    public class MySolutionWindowsFormsApplication : WinApplication {
        // ...
        protected override void CreateDefaultObjectSpaceProvider(CreateCustomObjectSpaceProviderEventArgs args) {
            AuditedDbContextFactory<DbContext> dbFactory = AuditedDbContextFactory.CreateFactory(typeof(MySolutionEFCoreDbContext), args.ConnectionString,
            (builder, connectionString) => {
                builder.UseSqlServer(connectionString);
                // Uncomment the following line if your application uses the Security System.
                //builder.UseSecurity((ISelectDataSecurityProvider)Security, TypesInfo);
            }
            // Uncomment the following line if your application uses the Security System.
            //,(options) => {
            //    options.AuditUserProvider = new AuditUserProvider(Security);
            //}
            );
            EFCoreObjectSpaceProvider efCoreObjectSpaceProvider = new EFCoreObjectSpaceProvider(dbFactory, TypesInfo);
            //or
            //SecuredEFCoreObjectSpaceProvider efCoreObjectSpaceProvider = new SecuredEFCoreObjectSpaceProvider((ISelectDataSecurityProvider)Security, dbFactory, TypesInfo);
            args.ObjectSpaceProviders.Add(efCoreObjectSpaceProvider);
            // ...
        }
    }
    
  5. Register the AuditDataItemPersistent and AuditEFCoreWeakReference types in the application DbContext:

    File: MySolution.Module\BusinessObjects\MySolutionDbContext.cs.

    public class MySolutionEFCoreDbContext : DbContext {
        // ...
        public DbSet<AuditDataItemPersistent> AuditData { get; set; }
        public DbSet<AuditEFCoreWeakReference> AuditEFCoreWeakReferences { get; set; }
        // ...
    }
    

    For more information on these classes, refer to the following help topic: Access the Audit Log In the Database.

  6. In applications with the Security System, configure permissions for non-administrative roles. This allows the Module to create audit records for changes that non-administrative users make. Additionally, you can allow users to read the information on their changes only.

    File: MySolution.Module\DatabaseUpdater\Updater.cs.

    using DevExpress.Persistent.BaseImpl.EFCore.AuditTrail;
    // ...
    public class Updater : ModuleUpdater {
        // ...
        public override void UpdateDatabaseAfterUpdateSchema() {
            base.UpdateDatabaseAfterUpdateSchema();
            // ...
            PermissionPolicyRole userRole = ObjectSpace.FirstOrDefault<PermissionPolicyRole>(role => role.Name == "Users");
            if(userRole == null) {
                // ...
                defaultRole.AddTypePermission<AuditDataItemPersistent>(SecurityOperations.Read, SecurityPermissionState.Deny);
                defaultRole.AddObjectPermissionFromLambda<AuditDataItemPersistent>(SecurityOperations.ReadWriteAccess, a => a.UserObject.Key == CurrentUserIdOperator.CurrentUserId().ToString(), SecurityPermissionState.Allow);
                defaultRole.AddTypePermission<AuditDataItemPersistent>(SecurityOperations.Create, SecurityPermissionState.Allow);
                defaultRole.AddTypePermission<AuditEFCoreWeakReference>(SecurityOperations.ReadWriteAccess, SecurityPermissionState.Allow);
                defaultRole.AddTypePermission<AuditEFCoreWeakReference>(SecurityOperations.Create, SecurityPermissionState.Allow);
            }
        }
    }
    

    Alternatively, you can create an additional DbContext to store audit records. In this scenario, you do not need to configure permissions as the Security System does not protect this DbContext. Refer to the following help topic for more information on this technique: Use an Additional DbContext to Access Audit Data.

  7. Run the application and click the Reports | Audit Event navigation item. The invoked List View contains change history for audited objects.

ASP.NET Core Blazor

Audit Event View in a Blazor application

WinForms

Audit Event View in a WinForms application

Note

The AuditInformationReadonlyViewController denies users the ability to create, edit, and delete IAuditDataItemPersistent objects in the UI.

Display Change History in the Object Detail View

Follow the steps below to show the history of an object in its Detail View:

  1. Implement the IObjectSpaceLink interface in your business class.
  2. Add the AuditDataItemPersistent collection property to your business class.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations.Schema;
    using DevExpress.ExpressApp;
    using DevExpress.Persistent.Base;
    using DevExpress.Persistent.BaseImpl.EFCore.AuditTrail;
    // ...
    [DefaultClassOptions]
    public class MyBusinessObject : IObjectSpaceLink {
        [Browsable(false)]
        public Int32 ID { get; protected set; }
        public string StringProperty { get; set; }
        IObjectSpace objectSpace;
        IObjectSpace IObjectSpaceLink.ObjectSpace {
            get { return objectSpace; }
            set { objectSpace = value; }
        }
        [CollectionOperationSet(AllowAdd = false, AllowRemove = false)]
        [NotMapped]
        public virtual IList<AuditDataItemPersistent> ChangeHistory {
            get { return AuditDataItemPersistent.GetAuditTrail(objectSpace, this); }
        }
    }
    

The following image demonstrates the result:

ASP.NET Core Blazor

Change History in a business object Detail View

WinForms

Change History in a business object Detail View

Tip

You can also access the audit log in the database directly if you do not want to display history in the UI.