Skip to main content
.NET 6.0+

Store Application Model Differences in the Database

  • 5 minutes to read

When you create a new application with the Security System enabled in the Solution Wizard, XAF stores user settings (model differences) in the database with the help of the ModelDifferenceDbStore storage (the default setting). This topic describes how to enable this feature in an existing application and how to store shared model differences (administrator settings) in the database.

Note

When the Security System is disabled, XAF passes the System.Security.Principal.WindowsIdentity.GetCurrent().Name value the IModelDifference.UserId property to use as user identifier. You can use the technique described in this article to enable ModelDifferenceDbStore in unsecured Windows Forms applications. We do not recommend that you enable ModelDifferenceDbStore in unsecured ASP.NET Web Forms applications, because in this case UserID is the same for all users. XAF supports shared model differences for ASP.NET Core Blazor, Windows Forms, and ASP.NET Web Forms when the Security System is disabled.

  1. Depending on the platform, navigate to one for the following files:

    • YourApplicationName.Win\WinModule.cs file located in your Windows Forms module or application project.
    • YourApplicationName.Web\WebModule.cs file located in the ASP.NET Web Forms module project.
    • YourApplicationName.Blazor.Server\BlazorModule.cs file located in the ASP.NET Core Blazor module or application project
  2. In the overridden ModuleBase.Setup method, subscribe to the XafApplication.CreateCustomModelDifferenceStore and XafApplication.CreateCustomUserModelDifferenceStore events.
  3. In these event handlers, pass the ModelDifferenceDbStore instance to the e.Store parameter. Pass the ModelDifference type to the ModelDifferenceDbStore constructor. Here, ModelDifference is a built-in persistent object from the DevExpress.Persistent.BaseImpl namespace (for XPO) or from the DevExpress.Persistent.BaseImpl.EF namespace (for Entity Framework Core) that implements the IModelDifference interface.
  4. Set the last parameter of the ModelDifferenceDbStore constructor (used to initialize the IModelDifference.ContextId property) to "Blazor" (ASP.NET Core Blazor), "Win" (Windows Forms), or "Web" (ASP.NET Web Forms). This is necessary to distinguish between model differences created for the same user on different platforms.

    public sealed partial class MySolutionBlazorModule : ModuleBase {
        private void Application_CreateCustomModelDifferenceStore(Object sender, CreateCustomModelDifferenceStoreEventArgs e) {
            e.Store = new ModelDifferenceDbStore((XafApplication)sender, typeof(ModelDifference), true, "Blazor");
            e.Handled = true;
        }
        private void Application_CreateCustomUserModelDifferenceStore(Object sender, CreateCustomModelDifferenceStoreEventArgs e) {
            e.Store = new ModelDifferenceDbStore((XafApplication)sender, typeof(ModelDifference), false, "Blazor");
            e.Handled = true;
        }
    
        // ...
        public override void Setup(XafApplication application) {
            base.Setup(application);
            application.CreateCustomModelDifferenceStore += Application_CreateCustomModelDifferenceStore;
            application.CreateCustomUserModelDifferenceStore += Application_CreateCustomUserModelDifferenceStore;
        }
    }
    

    Note

    When the CreateCustomModelDifferenceStore event is handled, the shared model differences (administrator settings) are stored in the database. XAF ignores changes in the Model.xafml file in the application project if the database record already exists for the shared model differences. To reload settings from Model.xafml, enable the administrative UI and use the Import Shared Model Difference Action (or delete the “Shared Model Difference” record and restart). If this behavior is inappropriate, do not handle this event. Handle it in the RELEASE project configuration only.

  5. Navigate to the YourSolutionName.Module\Module.cs file and add the following code to the Module constructor:

    using DevExpress.Persistent.BaseImpl;
    // ...
    namespace SimpleProjectManager.Module {
        public sealed partial class SimpleProjectManagerModule : ModuleBase {
            public SimpleProjectManagerModule() {
                // ...
                AdditionalExportedTypes.Add(typeof(ModelDifference));
                AdditionalExportedTypes.Add(typeof(ModelDifferenceAspect));
            }
        }
    }
    
  6. If you use Entity Framework Core, additionally register ModelDifference and ModelDifferenceAspect classes in DbContext.

    using DevExpress.Persistent.BaseImpl.EF;
    // ...
    public class MyDbContext : DbContext {
        // ...
        public DbSet<ModelDifference> ModelDifferences { get; set; }
        public DbSet<ModelDifferenceAspect> ModelDifferenceAspects { get; set; }
    }
    
  7. In the MySolution.Module\DatabaseUpdate\Updater.cs file, check that all users have read/write access to ModelDifference and ModelDifferenceAspect types.

    public class Updater : ModuleUpdater {
        public override void UpdateDatabaseAfterUpdateSchema() {
            base.UpdateDatabaseAfterUpdateSchema();
    
            PermissionPolicyRole defaultRole = ObjectSpace.FirstOrDefault<PermissionPolicyRole>(role => role.Name == "Default");
            if(defaultRole == null) {
                defaultRole = ObjectSpace.CreateObject<PermissionPolicyRole>();
                defaultRole.Name = "Default";
                defaultRole.AddObjectPermissionFromLambda<ApplicationUser>(SecurityOperations.Read, u => u.ID == (Guid)CurrentUserIdOperator.CurrentUserId(), SecurityPermissionState.Allow);
                defaultRole.AddNavigationPermission(@"Application/NavigationItems/Items/Default/Items/MyDetails", SecurityPermissionState.Allow);
                defaultRole.AddMemberPermissionFromLambda<ApplicationUser>(SecurityOperations.Write, "ChangePasswordOnFirstLogon", u => u.ID == (Guid)CurrentUserIdOperator.CurrentUserId(), SecurityPermissionState.Allow);
                defaultRole.AddMemberPermissionFromLambda<ApplicationUser>(SecurityOperations.Write, "StoredPassword", u => u.ID == (Guid)CurrentUserIdOperator.CurrentUserId(), SecurityPermissionState.Allow);
                defaultRole.AddTypePermissionsRecursively<PermissionPolicyRole>(SecurityOperations.Read, SecurityPermissionState.Deny);
                defaultRole.AddObjectPermission<ModelDifference>(SecurityOperations.ReadWriteAccess, "UserId = ToStr(CurrentUserId())", SecurityPermissionState.Allow);
                defaultRole.AddObjectPermission<ModelDifferenceAspect>(SecurityOperations.ReadWriteAccess, "Owner.UserId = ToStr(CurrentUserId())", SecurityPermissionState.Allow);
                // The 'Create' permission is additionally required if you use the Middle Tier Application Server
                defaultRole.AddTypePermissionsRecursively<ModelDifference>(SecurityOperations.Create, SecurityPermissionState.Allow);
                defaultRole.AddTypePermissionsRecursively<ModelDifferenceAspect>(SecurityOperations.Create, SecurityPermissionState.Allow);                
            }
            sampleUser.Roles.Add(defaultRole);
            // ...
            ObjectSpace.CommitChanges();
        }
        // ...
    }
    

Tip

For information on how you can enable UI elements to manage Model Differences stored in the database, refer to the following article - How to: Enable the Administrative UI for managing Users’ Model Differences.

Note

Thread-safe data layers have limitations. If you create a SecuredObjectSpaceProvider or an XPObjectSpaceProvider and set threadSafe to true in the constructor, the following features are unavailable:

These capabilities require your application to load information about custom persistent fields and then update the database schema. However, thread-safe data layer does not support data model modifications after a database connection is established.