Back to Devexpress

Optimistic Locking (Concurrency Control in EF Core)

expressappframework-405384-business-model-design-orm-business-model-design-with-entity-framework-core-optimistic-locking.md

latest10.9 KB
Original Source

Optimistic Locking (Concurrency Control in EF Core)

  • Jul 09, 2025
  • 5 minutes to read

XAF uses the optimistic concurrency control (OCC) mechanism to handle data conflicts.

Objects with optimistic locking contain a concurrency token field that updates with each change in the database. XAF compares the local token value with the database value when changes are submitted. If they match, local changes overwrite database values and XAF updates the token. If they differ, XAF merges changes automatically or offers conflict resolution options based on configuration settings.

To support optimistic locking, a business class must implement the IOptimisticLock interface.

Tip

This topic contains code samples based on the MainDemo Blazor Server demo application that ships with XAF. You can find this demo in the following folder: %PUBLIC%\Documents\DevExpress Demos 25.2\Components\XAF\MainDemo.NET.EFCore\CS\MainDemo.Blazor.Server.

Enable Optimistic Locking

Optimistic locking is available in new XAF applications out of the box for the BaseObject class and all its descendants.

In case of concurrent changes, XAF merges non-conflicting changes and displays a pop-up window with resolution options for conflicting changes.

To enable optimistic locking in an existing application, add the following code to the DbContext.OnModelCreating() method:

csharp
using DevExpress.Persistent.BaseImpl.EF;
using Microsoft.EntityFrameworkCore;

namespace MainDemo.Module.BusinessObjects;

public class MainDemoDbContext : DbContext {
    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        base.OnModelCreating(modelBuilder);
        //...
        modelBuilder.UseOptimisticLock()
    }
}

Configure Concurrency Options

Optimistic locking functionality offers two options to fine-tune conflict detection and resolution in your system:

OptimisticLockDetectionSpecifies whether XAF identifies conflicting changes using the object’s concurrency token or by assessing all object properties.OptimisticLockHandlingDefines how XAF handles conflicts: automatic merging or conflict resolution options.

You can specify both options for an object space in the application builder. These settings will affect your entire application.

csharp
using Microsoft.EntityFrameworkCore;

namespace MainDemo.Blazor.Server;

public class Startup {
    //...
    public void ConfigureServices(IServiceCollection services) {
        //...
        services.AddXaf(Configuration, builder => {
            builder.UseApplication<MainDemoBlazorApplication>();
            //...
            builder.ObjectSpaceProviders
                .AddSecuredEFCore(o => { o.PreFetchReferenceProperties();
                    o.OptimisticLockHandling = DevExpress.ExpressApp.DC.OptimisticLockHandling.Reload;
                    o.OptimisticLockDetection = DevExpress.ExpressApp.DC.OptimisticLockDetection.AllFields;
                })
        })
    }
}
csharp
using Microsoft.EntityFrameworkCore;

namespace MainDemo.Win;

public class ApplicationBuilder : IDesignTimeApplicationFactory {
    public static WinApplication BuildApplication() {
        //...
        builder.ObjectSpaceProviders
           .AddEFCore(options => {
               options.PreFetchReferenceProperties();
               options.OptimisticLockDetection = DevExpress.ExpressApp.DC.OptimisticLockDetection.AllFields;
               options.OptimisticLockHandling = DevExpress.ExpressApp.DC.OptimisticLockHandling.Reload; 
           })
    }
}

To specify custom behavior for a specific class, use the OptimisticLock attribute:

csharp
using Microsoft.EntityFrameworkCore;

namespace MainDemo.Module.BusinessObjects;

[OptimisticLock(OptimisticLockDetection = OptimisticLockDetection.AllFields,OptimisticLockHandling = OptimisticLockHandling.Reload)]
public class Department : BaseObject, ITreeNode {
    //...
}

Resolve Collisions Behavior

XAF offers a variety of conflict resolution strategies that depend on the combination of OptimisticLockHandling and OptimisticLockDetection values.

If you specify Default for OptimisticLockHandling or OptimisticLockDetection option, XAF disables conflict resolution and displays a warning message. When you refresh data, the database version overwrites local changes.

The following table covers other available combinations of the OptimisticLockDetection and OptimisticLockHanding options and describes XAF behavior when concurrency changes are detected:

OptimisticLockHandlingOptimisticLockDetection = OptimisticLockFieldOptimisticLockDetection = AllFields
NoneXAF displays a pop-up window with resolution options.If changes do not conflict, XAF displays a pop-up window with merge options. If changes are conflicting, XAF displays a pop-up window with resolution options.
MergeIf XAF detects changes, it displays a pop-up window with resolution options.If changes do not conflict, XAF merges the changes in the background. If changes are conflicting, XAF displays a pop-up window with resolution options.
IgnoreXAF overwrites the database object with the local version.XAF merges non-conflicting changes and overwrites conflicting database changes with the local values.
ReloadXAF overwrites the local object with the database version.XAF merges non-conflicting changes and overwrites conflicting local changes with the database values.

If a user modifies an object that was deleted from the database, XAF shows a warning message and deletes the local version.

Resolve Collision Dialog

XAF displays this pop-up window when it detects concurrency conflicting changes in an object and the resulting conflicts cannot be resolved automatically.

Apply My ChangesXAF applies the local version of conflicting changes and the database version of non-conflicting changes.Apply Their ChangesXAF takes the database version of conflicting changes and the local version of non-conflicting changes.Discard All My ChangesXAF overwrites all local changes with the database version.CancelXAF cancels the “Save” operation.

Merge Dialog

XAF displays this pop-up window when it detects concurrency non-conflicting changes in an object (for the combination of OptimisticLockDetection.AllFields and OptimisticLockHandling.None options).

MergeXAF merges changes.Discard All My ChangesXAF overwrites all local changes with the database version.CancelXAF cancels the “Save” operation.

Conflict Warning Message

XAF displays this warning when:

  • Conflict resolution is disabled.
  • When a user attempts to modify a deleted object.
  • When a user attempts to delete a modified object.

Customize Collision Behavior Dynamically at Runtime

XAF includes a ProcessDataLockingInfoController. Use this controller to implement your own collision handling strategies. Handle the DataLockingProcessing event to manage information about modified records and collisions.

The following code snippet enables automatic merge in XAF applications:

csharp
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.SystemModule;

namespace MainDemo.Module.Controllers;

public class AutoMergeViewController : ViewController {
    private ProcessDataLockingInfoController lockController;

    private void OnDataLocking(object sender, DataLockingProcessingEventArgs e) {
        foreach (var info in e.DataLockingInfo.ObjectLockingInfo) {
            info.CanAutoMerge = info.CanMerge;
        }
    }

    protected override void OnActivated() {
        base.OnActivated();
        lockController = Frame.GetController<ProcessDataLockingInfoController>();
        lockController.DataLockingProcessing += OnDataLocking;
    }

    protected override void OnDeactivated() {
        base.OnDeactivated();
        if (lockController != null) {
            lockController.DataLockingProcessing -= OnDataLocking;
            lockController = null;
        }
    }
}

Disable Optimistic Locking For an Entire Application

To disable optimistic locking in an entire application, remove modelBuilder.UseOptimisticLock(); from the DbContext.OnModelCreating() method:

csharp
using DevExpress.Persistent.BaseImpl.EF;
using Microsoft.EntityFrameworkCore;

namespace MainDemo.Module.BusinessObjects;

public class MainDemoDbContext : DbContext {
    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        base.OnModelCreating(modelBuilder);
        //...
        modelBuilder.UseOptimisticLock();

        //...
    }
}

Disable Optimistic Locking For a Specific Class

To disable optimistic locking for a specific class, add the OptimisticLockIgnore attribute to the class declaration:

csharp
using Microsoft.EntityFrameworkCore;

namespace MainDemo.Module.BusinessObjects;

[OptimisticLockIgnore]
public class Department : BaseObject {
    //...
}

Disable Optimistic Locking For a Specific Property

To disable optimistic locking for a specific property, decorate the property with the OptimisticLockIgnore attribute:

csharp
using Microsoft.EntityFrameworkCore;

namespace MainDemo.Module.BusinessObjects;

public class Department : BaseObject {
    //...
    [OptimisticLockIgnore]
    public virtual string Office { get; set; }
}