How to: Store the Application Model Differences in the Database

When you create a new application with the Security System enabled in the Solution Wizard, the end-user settings (model differences) are stored in the database using the ModelDifferenceDbStore storage, by default. This topic describes how to enable this feature in an existing application, along with how to store shared model differences (administrator's settings) in the database.

Note

Mobile applications do not allow you to store model differences in a database, so the approach described in this topic is not supported by the Mobile platform.

The System.Security.Principal.WindowsIdentity.GetCurrent().Name value is used as a user identifier (passed to the IModelDifference.UserId property) when the Security System is disabled. So, you can enable ModelDifferenceDbStore for WinForms applications with the disabled Security System using the approach described here. However, it is not recommended that you enable ModelDifferenceDbStore for unsecured ASP.NET applications, because the UserID will be the same for all users in this instance. Shared model differences are supported for both WinForms and ASP.NET when the Security System is disabled.

Edit the WinModule.cs (WinModule.vb) file located in the WinForms module 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 the e.Store parameter. Pass the ModelDifference type to the ModelDifferenceDbStore constructor. Here, the 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) implementing the IModelDifference interface. Note the constructor's contextId last parameter (used to initialize the IModelDifference.ContextId property). In the WinForms module, set it to "Win" to distinguish model differences created for the same user in different platforms.

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

Analogously, edit the WebModule.cs (WebModule.vb) file located in the ASP.NET module project, but set the contextId parameter to "Web" instead of "Win".

public sealed partial class MySolutionAspNetModule : ModuleBase {
    private void Application_CreateCustomModelDifferenceStore(Object sender, CreateCustomModelDifferenceStoreEventArgs e) {
        e.Store = new ModelDifferenceDbStore((XafApplication)sender, typeof(ModelDifference), true, "Web");
        e.Handled = true;
    }
    private void Application_CreateCustomUserModelDifferenceStore(Object sender, CreateCustomModelDifferenceStoreEventArgs e) {
        e.Store = new ModelDifferenceDbStore((XafApplication)sender, typeof(ModelDifference), false, "Web");
        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. Changes with the Model.xafml file located in the application project are ignored 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.

If you use the Entity Framework, register the ModelDifference and ModelDifferenceAspect entity types within your DbConext.

using DevExpress.Persistent.BaseImpl.EF;
// ...
public class MyDbContext : DbContext {
    // ...
    public DbSet<ModelDifference> ModelDifferences { get; set; }
    public DbSet<ModelDifferenceAspect> ModelDifferenceAspects { get; set; }
}

I you use XPO, run the Module Designer and add the ModelDifference and ModelDifferenceAspect persistent types to the Exported Types section.

Ensure 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.FindObject<PermissionPolicyRole>(new BinaryOperator("Name", "Default"));
        if(defaultRole == null) {
            defaultRole = ObjectSpace.CreateObject<PermissionPolicyRole>();
            defaultRole.Name = "Default";
            defaultRole.AddObjectPermission<PermissionPolicyUser>(SecurityOperations.Read, "[Oid] = CurrentUserId()", SecurityPermissionState.Allow);
            defaultRole.AddNavigationPermission(@"Application/NavigationItems/Items/Default/Items/MyDetails", SecurityPermissionState.Allow);
            defaultRole.AddMemberPermission<PermissionPolicyUser>(SecurityOperations.Write, "ChangePasswordOnFirstLogon", "[Oid] = CurrentUserId()", SecurityPermissionState.Allow);
            defaultRole.AddMemberPermission<PermissionPolicyUser>(SecurityOperations.Write, "StoredPassword", "[Oid] = CurrentUserId()", SecurityPermissionState.Allow);
            defaultRole.AddTypePermissionsRecursively<PermissionPolicyRole>(SecurityOperations.Read, SecurityPermissionState.Deny);
            defaultRole.AddTypePermissionsRecursively<ModelDifference>(SecurityOperations.ReadWriteAccess, SecurityPermissionState.Allow);
            defaultRole.AddTypePermissionsRecursively<ModelDifferenceAspect>(SecurityOperations.ReadWriteAccess, 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

Refer to the How to: Enable the Administrative UI for managing Users' Model Differences topic to learn how to enable UI elements for managing Model Differences stored in the database.

Note

The following combination of features is not supported when used together.

In 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.