Skip to main content

Store Application Model Differences in the Database

  • 5 minutes to read

When you create a new application with the Security System enabled in the Template Kit, 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

The following features should not be used together:

With this configuration, your application loads information on custom persistent fields from the database and then updates the database schema. However, a thread-safe data layer does not support altering the data model after the database connection is established.