Example E2620
Visible to All Users

How to generate a sequential number for a business object within a database transaction

Files to look at:

This is a general solution, showing how to use the ExplicitUnitOfWork class to generate sequential numbers that can then be used as user-friendly identifiers in documents, as invoice line numbers, etc. The ExplicitUnitOfWork class is used to ensure that the assignment of the sequential number will be a part of a database transaction that results in the successful saving of the document.
Hence, the current solution will also work in a scenario with high-volume of concurrency transactions.

See also:
XAF: How to generate and assign a sequential number for a business object within a database transaction, while being a part of a successful saving process (XAF)

Does this example address your development requirements/objectives?

(you will be redirected to DevExpress.com to submit your response)

Example Code

ExplicitUnitOfWorkDemo/frmMain.cs(vb)
C#
using System; using DevExpress.Xpo; using DevExpress.XtraEditors; using System.Collections.Generic; namespace ExplicitUnitOfWorkDemo { public partial class frmMain : XtraForm { public frmMain() { InitializeComponent(); } private void btnRecreateAddress_Click(object sender, EventArgs e) { ViewRecord row = gridView1.GetFocusedRow() as ViewRecord; if(row == null) return; long personOid = RecreateAddress(row); UpdateRows(); gridView1.SetFocusedRowCellValue(colOid, personOid); } private static long RecreateAddress(ViewRecord selectedRow) { long personOid; using (ExplicitUnitOfWork explicitUnitOfWork = new ExplicitUnitOfWork(DatabaseHelper.DataLayer)) { //Get selected person object. Person person = explicitUnitOfWork.GetObjectByKey<Person>(selectedRow["Oid"]); if(person == null || person.Address == null) throw new ArgumentNullException("person"); //Remember person key value. personOid = person.Oid; Address address = person.Address; long addressOid = address.Oid; List<Person> referenceList = new List<Person>(address.Persons); //Reset references to the Address object. for (int i = 0; i < referenceList.Count; i++) { referenceList[i].Address = null; } //Delete the Address object. explicitUnitOfWork.Delete(address); //Save changes to the database in case of an explicit transaction. explicitUnitOfWork.FlushChanges(); //Create a new instance of the Address object. address = DatabaseHelper.CreateNewAddress(explicitUnitOfWork, addressOid); //Recover references to the Address object. for (int i = 0; i < referenceList.Count; i++) { referenceList[i].Address = address; } explicitUnitOfWork.CommitChanges(); } return personOid; } private void btnCreatePerson_Click(object sender, EventArgs e) { CreatePerson(); UpdateRows(); } private static void CreatePerson() { long personOid; using (UnitOfWork uow = new UnitOfWork(DatabaseHelper.SequenceDataLayer)) { Address address; using (SequenceGenerator<Address> sg = new SequenceGenerator<Address>(DatabaseHelper.SequenceDataLayer)) { address = DatabaseHelper.CreateNewAddress(uow, sg.GetNextId()); uow.CommitChanges(); sg.Accept(); } using (SequenceGenerator<Person> sg = new SequenceGenerator<Person>(DatabaseHelper.SequenceDataLayer)) { Person person = DatabaseHelper.CreateNewPerson(uow, sg.GetNextId()); personOid = person.Oid; person.Address = address; uow.CommitChanges(); sg.Accept(); } } } private void btnCreatePersons_Click(object sender, EventArgs e) { CreatePersons(); UpdateRows(); } private static void CreatePersons() { using (UnitOfWork uow = new UnitOfWork(DatabaseHelper.SequenceDataLayer)) { List<Address> addressList = new List<Address>(); using (SequenceGenerator<Address> sg = new SequenceGenerator<Address>(DatabaseHelper.SequenceDataLayer)) { for (int i = 0; i < 10; i++) { Address address = DatabaseHelper.CreateNewAddress(uow, sg.GetNextId()); addressList.Add(address); } uow.CommitChanges(); sg.Accept(); } using (SequenceGenerator<Person> sg = new SequenceGenerator<Person>(DatabaseHelper.SequenceDataLayer)) { for (int i = 0; i < 10; i++) { for (int k = 0; k < 10; k++) { Person person = DatabaseHelper.CreateNewPerson(uow, sg.GetNextId()); person.Address = addressList[i]; } } uow.CommitChanges(); sg.Accept(); } } } private void btnUpdate_Click(object sender, EventArgs e) { UpdateRows(); } private void UpdateRows() { xpView1.TopReturnedRecords = (int)spinEdit1.Value; xpView1.Reload(); } private void btnClearDB_Click(object sender, EventArgs e) { ClearDatabase(); UpdateRows(); } private static void ClearDatabase() { using (UnitOfWork uow = new UnitOfWork(DatabaseHelper.DataLayer)) { XPCollection<Person> personList = new XPCollection<Person>(uow); XPCollection<Address> addressList = new XPCollection<Address>(uow); XPCollection<Sequence> sequencList = new XPCollection<Sequence>(uow); uow.Delete(personList); uow.Delete(addressList); uow.Delete(sequencList); uow.CommitChanges(); } } } }
ExplicitUnitOfWorkDemo/SequenceGenerator.cs(vb)
C#
using System; using DevExpress.Xpo; using DevExpress.Xpo.Metadata; using DevExpress.Xpo.DB.Exceptions; namespace ExplicitUnitOfWorkDemo { //This class is used to generate sequential numbers for persistent objects. //Use the GetNextId method to get the next number and the Accept method, to save these changes to the database. public class SequenceGenerator<T> : IDisposable { private ExplicitUnitOfWork euow; private XPClassInfo classInfo; private Sequence seq; public SequenceGenerator(IDataLayer dataLayer) { euow = new ExplicitUnitOfWork(dataLayer); classInfo = euow.GetClassInfo<T>(); } public void Accept(){ euow.CommitChanges(); } public long GetNextId() { long nextId; while(true) { try { if(seq == null) { seq = euow.GetObjectByKey<Sequence>(classInfo.FullName, true); if(seq == null) { seq = new Sequence(euow); seq.TypeName = classInfo.FullName; seq.NextId = 0; } } nextId = seq.NextId; seq.NextId++; euow.FlushChanges(); } catch(LockingException) { seq = null; continue; } break; } return nextId; } public void Close() { if(euow != null) euow.Dispose(); } void IDisposable.Dispose() { Close(); } } //This persistent class is used to store last sequential number for persistent objects. public class Sequence : XPBaseObject { private string typeName; private long nextId; public Sequence(Session session) : base(session) { } [Key] public string TypeName { get { return typeName; } set { SetPropertyValue("TypeName", ref typeName, value); } } public long NextId { get { return nextId; } set { SetPropertyValue("NextId", ref nextId, value); } } } }

Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.