Auto-Generate Unique Number Sequence
- 4 minutes to read
Orders, Invoices, Articles, and other business entities often require an auto-filled Number or Code field that users can memorize. Although these user-friendly field values are sequential, gaps are permitted (for example, when a user deletes an order). Use one of the following techniques to use this field in XAF applications:
- Database-Level: Auto-Increment Database Column
- This solution avoids sequence gaps. Since this solution is implemented at the database level, it is specific to the database type.
- ORM-Level: Programmatic Transaction (XPO Only)
- This solution is more complicated and implemented at the ORM level. It does not depend on the database type and also avoids sequence gaps.
Database-Level: Auto-Increment Database Column
EF Core
Use the following steps to generate sequential values for business object properties to ensure that these properties are assigned a value when their respective objects are saved to the database:
Add the
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
attribute to the required business object property:public class Address : BaseObject { // ... [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public virtual long SequentialNumber { get; set; } // ... }
Add the following code snippet to the DbContext’s
OnModelCreating
method implementation to ensure that the generated values are always displayed in the UI immediately after the object has been saved:public class GenerateUserFriendlyIdEFCoreDbContext : DbContext { // ... protected override void OnModelCreating(ModelBuilder modelBuilder) { // ... modelBuilder.Entity<Address>().UsePropertyAccessMode(PropertyAccessMode.FieldDuringConstruction); } }
XPO
Use the FetchOnly attribute as follows:
- Create an auto-increment database column (for example, Identity in SQL Server).
- Create a persistent property mapped to this column and decorate it with the
FetchOnly
attribute.
ORM-Level: Programmatic Transaction (XPO Only)
The following example shows how to implement the Programmatic Transaction solution:
Refresh the Identifier Field Value in the UI
If you use the Programmatic Transaction solution with UI-Level security, an application displays the identifier field value immediately after a user creates a new object. In the Integrated Mode and Middle-Tier Application Server configuration, the newly generated sequence number is displayed only after a manual refresh. XAF does not pass the sequence to the client immediately. Implement one of the techniques described below to refresh the field value automatically.
Override the Business Class OnSaved Method
Override your business class OnSaved
method to update the value on the client side:
namespace MySolution.Module.BusinessObjects {
public class YourBusinessClass : YourBaseXpoClass {
// ...
protected override void OnSaved() {
base.OnSaved();
if(Number == 0) { // 0 is the default value of the sequence number property.
Session.Reload(this);
}
}
// ...
}
}
Use a Custom Controller to Reload the Object Space
Use a custom Controller to reload the Object Space after new objects are saved:
namespace MySolution.Module.Controllers {
// ...
public class RefreshAfterCommitController : ViewController {
bool needRefresh = false;
public RefreshAfterCommitController() {
TargetViewNesting = Nesting.Root;
}
protected override void OnActivated() {
base.OnActivated();
ObjectSpace.Committed += ObjectSpace_Committed;
ObjectSpace.Committing += ObjectSpace_Committing;
ObjectSpace.Reloaded += ObjectSpace_Reloaded;
}
protected override void OnDeactivated() {
ObjectSpace.Committed -= ObjectSpace_Committed;
ObjectSpace.Committing -= ObjectSpace_Committing;
ObjectSpace.Reloaded -= ObjectSpace_Reloaded;
base.OnDeactivated();
}
private void ObjectSpace_Reloaded(object sender, EventArgs e) {
needRefresh = false;
}
private void ObjectSpace_Committing(object sender, System.ComponentModel.CancelEventArgs e) {
var objectSpace = (IObjectSpace)sender;
foreach (var obj in objectSpace.GetObjectsToSave(false)) {
if (objectSpace.IsNewObject(obj)) {
needRefresh = true; break;
}
}
}
private void ObjectSpace_Committed(object sender, EventArgs e) {
if (needRefresh) {
((IObjectSpace)sender).Refresh();
needRefresh = false;
}
}
}
}