Skip to main content
A newer version of this page is available. .
All docs
V21.2

Save Objects to Database

  • 8 minutes to read

Common business tasks need perform additional logic before storing changes in a database. For example, you may need to get objects that are about to save and write additional information to them or save objects in a custom way.

In a Controller

Useful API

Members

Description

Methods:

IObjectSpace.CommitChanges

Saves all the changes made to the persistent objects belonging to the current Object Space to the database.

IObjectSpace.IsObjectToSave

Indicates whether the specified object has been added, deleted or modified, but not committed in the transaction currently in progress.

IObjectSpace.GetObjectsToSave

Returns a collection of persistent objects that will be saved when the current transaction is committed, including objects that will be saved in the parent transaction(s), optionally.

IObjectSpace.RemoveFromModifiedObjects

Removes the specified object from the list of objects to be committed.

Properties:

IObjectSpace.IsCommitting

Indicates whether the Object Space is currently committing the changes made to its object(s).

IObjectSpace.ModifiedObjects

Returns a collection of objects that have been created, modified or deleted after they were retrieved or committed.

Events:

IObjectSpace.Committing

Occurs before persistent objects are saved to the database.

IObjectSpace.Committed

Occurs after persistent object changes are stored in the database.

IObjectSpace.CustomCommitChanges

Replaces the default commit logic with a custom one.

IObjectSpace.ObjectSaving

Occurs before saving changes made to a specified persistent object to the database.

IObjectSpace.ObjectSaved

Occurs after saving changes made to a specified persistent object to the database.

XAF saves changes in a View when the Save, SaveAndNew, and SaveAndClose Actions are executed and when the Accept Action in a popup window is executed if the DialogController.SaveOnAccept property is true. In XAF WinForms applications, you can control if XAF automatically saves or rollbacks changes when you change the current object or close a View. The ModificationsController.ModificationsHandlingMode property specifies this behavior.

Persist Changes in Code

To save changes in your code, call the IObjectSpace.CommitChanges method.

using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Actions;

public class MyViewController : ObjectViewController<DetailView, Department> {
    public MyViewController() {
        var action = new SimpleAction(this, "Change Title and Save changes", DevExpress.Persistent.Base.PredefinedCategory.Save);
        action.Execute += Action_Execute;
    }
    private void Action_Execute(object sender, SimpleActionExecuteEventArgs e) {
        Department department = (Department)View.CurrentObject;
        department.Title = "New Title";
        this.ObjectSpace.CommitChanges();
    }
}

Obtain Modified Objects

To write additional information to an object before saving it to a database, do this in the IObjectSpace.Committing event handler. The following code illustrates this. An Employee business object has the Notes property that stores audit information on a number of other objects that were changed in a current transaction.

using DevExpress.ExpressApp;
using System.Collections;

public class MyViewController : ObjectViewController<DetailView, Employee> {
    protected override void OnActivated() {
        base.OnActivated();
        ObjectSpace.Committing += ObjectSpace_Committing;
    }
    void ObjectSpace_Committing(object sender, System.ComponentModel.CancelEventArgs e) {
        ICollection collection = ObjectSpace.GetObjectsToSave(false);
        Employee owner = (Employee)View.CurrentObject;
        owner.Notes = string.Format("{0};{1} objects changed", owner.Notes, collection.Count);
    }
}

Process Saving Objects

It’s also possible to save changes in a custom way in the IObjectSpace.CustomCommitChanges event handler. To prevent the default logic to persist objects, handle the IObjectSpace.Committing event and call the IObjectSpace.RemoveFromModifiedObjects method to remove an object from being committed in the event handler.

The following code shows how to manually save child collection changes made in the parent Detail View:

using DevExpress.ExpressApp;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using YourSolutionName.Module.BusinessObjects;

namespace YourSolutionName.Module.Controllers {
    public class MyViewController : ObjectViewController<DetailView, Parent> {
        List<Child> detailObjectsCache = new List<Child>();
        protected override void OnActivated() {
            base.OnActivated();
            ObjectSpace.CustomCommitChanges += ObjectSpace_CustomCommitChanges;
            ObjectSpace.Committing += ObjectSpace_Committing;
            ObjectSpace.Committed += ObjectSpace_Committed;
            ObjectSpace.Reloaded += ObjectSpace_Reloaded;
        }
        protected override void OnDeactivated() {
            ObjectSpace.CustomCommitChanges -= ObjectSpace_CustomCommitChanges;
            ObjectSpace.Committing -= ObjectSpace_Committing;
            ObjectSpace.Committed -= ObjectSpace_Committed;
            ObjectSpace.Reloaded -= ObjectSpace_Reloaded;
            base.OnDeactivated();
        }
        void ObjectSpace_Reloaded(object sender, EventArgs e) {
            detailObjectsCache.Clear();
        }
        void ObjectSpace_Committed(object sender, EventArgs e) {
            detailObjectsCache.Clear();
        }
        void ObjectSpace_Committing(object sender, CancelEventArgs e) {
            IObjectSpace os = (IObjectSpace)sender;
            for (int i = os.ModifiedObjects.Count - 1; i >= 0; i--) {
                object item = os.ModifiedObjects[i];
                if (typeof(Child).IsAssignableFrom(item.GetType())) {
                    detailObjectsCache.Add(item as Child);
                    os.RemoveFromModifiedObjects(item);
                }
            }
        }
        void ObjectSpace_CustomCommitChanges(object sender, HandledEventArgs e) {
            // Implement custom logic to save Detail objects here. 
            foreach (Child detailObject in detailObjectsCache) {
                //...  
            }
        }
    }
}

Note

We recommend handling these events only for root Views. Nested Views use their parent View’s Object Space. As a result, the events will be handled several times. You can specify the controller scope as described at Define the Scope of Controllers and Actions. Alternatively, you can check properties like View.IsRoot and IObjectSpace.Owner in event handlers to determine for what View a handler is invoked.

In a Data Model (in an XPO Business Class)

In XPO business objects, additional logic when saving data is performed in the overridden OnSaved method of a business class. One of common tasks is to generate an auto incremented number or a sequence. Find a sample for how to implement this at XAF & XPO best practices: Unique auto increment/sequence number generation.

Be aware of specifics with implementing custom logic when you use Middle Tier Security.

  • The OnSaving and OnDeleting methods of a business class can be called multiple times because this Security mode uses more than one Session/DbContext object. If you implement custom logic in these methods, check whether a new value is already assigned to a property. This helps you avoid incorrect results.
  • Detail Views do not display changes made to an object within a transaction (for example, an auto-generated sequential number for an XPO business object) even you saved this object in a View. These changes are made on the server only and are not automatically passed to the client application. To show these changes, reload the object. If you want to reload the object on each Save operation, override the business class’s OnSaved method. The following example demonstrates how to override this method to update a reload an object on the client:

    using DevExpress.Persistent.BaseImpl;
    // ...
    public class DemoObject : BaseObject {
        // ...
        protected override void OnSaved() {
            base.OnSaved();
            // The 0 is default value
            if (Number == 0) {
                Session.Reload(this);
            }
        }
    }
    
  • The Session.DataLayer property is null in the secured Session. Instead of DataLayer, we recommend that you use the View.ObjectSpace property to query and change data from the database. This approach is also recommended for non-secure applications.

    If this recommendation does not apply to your scenario, use the Session.ObjectLayer property instead of DataLayer.

    You can also execute different code on the server and client depending on the DataLayer and ObjectLayer property values. The following example demonstrates how to do it:

    using DevExpress.ExpressApp.Security.ClientServer;
    using DevExpress.Persistent.BaseImpl;
    // ...
    public class DemoObject : BaseObject {
        // ...
        protected override void OnSaving() { 
            if(Session.DataLayer != null && !(Session.ObjectLayer is SecuredSessionObjectLayer)) { 
                // Server-side code
            } 
            else {  
                // Client-side code 
            } 
            base.OnSaving(); 
        }
    }