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.
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
- In the overridden ModuleBase.Setup method, subscribe to the XafApplication.CreateCustomModelDifferenceStore and XafApplication.CreateCustomUserModelDifferenceStore events.
- In these event handlers, pass the
ModelDifferenceDbStore
instance to thee.Store
parameter. Pass theModelDifference
type to theModelDifferenceDbStore
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. 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.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)); } } }
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; } }
In the MySolution.Module\DatabaseUpdate\Updater.cs file, check that all users have read/write access to
ModelDifference
andModelDifferenceAspect
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:
Application-wide model differences stored in the database using the XafApplication.CreateCustomModelDifferenceStore event. You can still store user-specific differences in the database using the XafApplication.CreateCustomUserModelDifferenceStore event.
Custom Persistent Fields declared in the application-level model differences.
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.