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:cspublic 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:cspublic 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;
}
}
}
}