Quantcast
Channel: DevExpress Support Center (Examples)
Viewing all articles
Browse latest Browse all 7205

How to generate a sequential and user-friendly identifier field within an XPO business class

$
0
0

Scenario

Orders, articles or other business entities often require that you have user-friendly Id or Code fields that end-users can memorize and use during phone conversations. They are usually sequential, but some gaps can be allowed as well (e.g., when an order is deleted). Refer to this StackOverFlow thread for more information on this common scenario, and a possible implementation.

Steps to implement

1. Add a new business class to your platform-agnostic module, and  call the static DevExpress.Persistent.BaseImpl.DistributedIdGeneratorHelper.Generate method from the AfterConstruction, OnSaving or other appropriate places within your persistent class or even Controller as shown in the Solution2.Module\BusinessObjects\Order.xx file. Depending on your business requirements, you can implement a readonly persistent or editable property where the generated value will be stored as well as check various conditions before generating the next sequence (e.g., string.IsNullOrEmpty(currentSequenceProperty) - to avoid double assignments, Session.IsNewObject(this) - to check whether the object is new, !(Session is NestedUnitOfWork) - to check whether the object is being saved to the database and not to the parent session, security system checks as per this blog post, etc.)

2. Build your platform-agnostic project and double-click on the Solution2.Module\Module.xx file to invoke the Module Designer;

3. Refer to the Exported Types section within the designer and expand the Referenced Assemblies | DevExpress.Persistent.BaseImpl node;

4. Select and press the space bar on the OidGenerator node to include this persistent type into the business model of your module:

 

IMPORTANT NOTES
1. The DistributedIdGeneratorHelper class demonstrated in this solution creates the IDGeneratorTable table to store the information about the last sequential number of a type. You can learn more on how this works from its source code at "C:\Program Files (x86)\DevExpress 1X.X\Components\Sources\DevExpress.Persistent\DevExpress.Persistent.BaseImpl\IDGenerator.cs".
Although this particular solution is simpler to implement and maintain (as it uses built-in XAF classes) than the 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) example, it is pretty safe and should also work in the most typical business scenarios (except for the middle-tier Application Server scenario, because the Session.DataLayer property is not initialized in this case). You can modify the example code as shown below to bypass this:

[C#]
protectedoverridevoidOnSaving(){if(!(SessionisNestedUnitOfWork)&&Session.IsNewObject(this)){if(string.IsNullOrEmpty(Code)){intnextSequence=DistributedIdGeneratorHelper.Generate(GetDataLayer(Session),this.GetType().FullName,"test2");Code=string.Format("N{0:D6}",nextSequence);}}base.OnSaving();}publicIDataLayerGetDataLayer(SessionsourceSession){Guard.ArgumentNotNull(sourceSession,"sourceSession");returnsourceSession.DataLayer==null?newSession(sourceSession.ObjectLayer).DataLayer:sourceSession.DataLayer;}


2. If you have validation rules for your business class, the OnSaving method (and thus the sequence generation) is called only after all validation is passed. However, in rare cases, if a database related error is thrown during the first save and then the form is re-saved, the OnSaving method may be called again and a new sequence can be generated. This may lead to gaps in sequential numbers, which is not always allowed by business requirements (e.g., government regulations). To avoid this, you can use a more complicated solution from the 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) example or implement a database-level solution to handle such situations (e.g., using triggers).

3. You can find functional EasyTest scripts for this scenario in the Solution2.Module\FunctionalTests\E4904.ets file.

Question Comments

Added By: Gareth- at: 10/2/2013 5:58:17 AM    

Thanks, works perfectly - I've seeded the ID by manually changing the [Oid] in the IDGeneratorTable, do I need to worry about the [OptimisicLockField] or will this update automatically?

Added By: Dennis (DevExpress Support) at: 10/2/2013 7:10:50 AM    

@Gareth: No, you do not need to care about the OptimisticLockField.

Added By: MohammedFarooq at: 12/2/2013 1:07:31 AM    

Hi,

The code is really helpful but it increments the value of code even if the record is deleted! How can we stop the code to increase its value when a record is deleted?

Added By: MohammedFarooq at: 12/2/2013 1:25:16 AM    

I manage to figure out the solution. I have just added the IsDeleted checking and it worked like a charm!!!

    Protected Overrides Sub OnSaving()
        If (Not IsDeleted) Then
             Me.codeCore = String.Format("N{0:D6}", DistributedIdGeneratorHelper.Generate(Me.Session.DataLayer, Me.GetType().FullName, String.Empty))
        End If
        MyBase.OnSaving()
    End Sub

Added By: Dennis (DevExpress Support) at: 12/2/2013 1:26:50 AM    

@Mohammed: Yes, your solution is correct. I will consider updating this example accordingly. Thanks for your input in this regard.

Added By: rushdan . at: 11/6/2014 12:02:48 AM    

Hallo

I do not understand about explanation. What I know is, after I run the application, then save the data , it will execute Code number automatically.

Is it this example to show how to execute auto increase number ? Thanks

Added By: Dennis (DevExpress Support) at: 11/6/2014 2:00:43 AM    

@Rushdan: With the current implementation, the generation code is executed when the persistent object is being saved (OnSaving). I do not quite understand your last question. The functionality implemented in this example is well-detailed in the description and it can also be seen from simple code.

Added By: Andrew Bingham 2 at: 2/5/2015 12:40:25 AM    

"override its AfterConstruction method, as shown in the Solution2.Module\BusinessObjects\DomainObject1.xx file"

1. I cannot find the DomainObject1 file in the sample
2. The only BusinessObject is Order.cs which does not have an AfterConstruction method
3. What code need to go in AfterConstruction method?
4. What are the advantages / disadvantages compared to E2829?

Added By: Dennis (DevExpress Support) at: 2/5/2015 12:47:04 AM    @Andrew: Thanks for your feedback.
1-3. I've corrected this misprint in the description. 
3. This particular solution is simpler to implement than E2829.Added By: Vishwas Mokashi at: 4/21/2015 8:52:32 AM    

Is there any provision so that we can restart the Sequence Number after say an Year is completed?. Also, any way to give the start number for the sequence?.

Added By: Dennis (DevExpress Support) at: 4/21/2015 8:57:35 AM    

@Vishwas: Sure, you can do this. Technically, the information about the last sequence number is stored in the Oid column of the IDGeneratorTable table with the help of the OidGenerator persistent class. You can modify this data using either ADO.NET or XPO means.

Added By: Vishwas Mokashi at: 4/21/2015 9:02:11 AM    

Ok thanks Dennis...will try

Added By: MohammedFarooq at: 10/14/2015 2:37:53 AM    

Dear Dennis,

I have applied this logic in my project which also has validation enabled. Now the issue is, if the validation of the document fails while saving the new number is generated. And when the user saves the document again then a new number is generated as well.

Added By: Dennis (DevExpress Support) at: 10/14/2015 2:58:59 AM    @Mohammed: The original example has the if(string.IsNullOrEmpty(Code))  check that prevents multiple generations. Have you applied this code in your project? If this does not help, submit a separate ticket and attach your problematic sample so we can assist you further.Added By: MohammedFarooq at: 10/14/2015 5:03:57 AM    

You hit a bull's eye! I didn't have the  if(string.IsNullOrEmpty(Code)) in my code.

Was it added recently? Because i followed this example before and somehow i missed it.

Added By: Dennis (DevExpress Support) at: 10/14/2015 5:18:38 AM    You're always welcome. AFAIK, I added it a year ago or so.Added By: Paul Kubb at: 3/16/2016 2:14:58 AM    Would you please describe more in detail about serverprefix table? how and when to use it?
I cannot find any documentation about it also.Added By: Dennis (DevExpress Support) at: 3/17/2016 1:57:56 AM    

@Paul: It was a mistake in the original description, because this table is actually unused by the DistributedIdGeneratorHelper class. Please accept my apologies for the confusion. As for the "string serverPrefix" parameter of the Generate method, it allows you to run several sequences in parallel. Refer to the "C:\Program Files (x86)\DevExpress 1X.X\Components\Sources\DevExpress.Persistent\DevExpress.Persistent.BaseImpl\IDGenerator.cs" source file for more details.

Added By: Paul Kubb at: 8/4/2016 9:44:09 AM    I have read a source file and have several questions aboud DistributedIdGeneratorHelper.Generate():

1.  it seems like you intend to create a separate new session, attempt to save it to the database separately until success and return its Oid. Why can't we use the bo's existing session here?

2. by this design, do you intend to avoid some multi-user problem or something? if so, please explain why having a separate session will overcome it

3. if the OidGenerator is saved successfully in a separate session but the main bo's save event is dead, will the OidGenerator's save event be rolled back? or will we have a jumpy running number instead?

thanks in advance

Added By: Dennis (DevExpress Support) at: 8/5/2016 7:12:09 AM    

@Paul:
A1-2: Your understanding of class design is correct. A separate session will help to avoid concurrency issues in a multi-user environment: if another user generated a new number while you were saving your own, the locking exception occurs and a new attempt to generate the next number for you starts in the loop.
A3:No, it will not. See my answer in the T304004 ticket for more details.

Added By: wang yu 4 at: 9/23/2016 12:59:54 AM    does it support middle tier ?Added By: Dennis (DevExpress Support) at: 9/23/2016 2:13:47 AM    

@Wang: As in the example's description, the DistributedIdGeneratorHelper class currently does not support the middle-tier Application Server scenario, because the Session.DataLayer property is not initialized in this case. I have added a code snipped in point #1 of the example's notes to show a possible way to bypass this behavior. Please let us know how this works for you.


Viewing all articles
Browse latest Browse all 7205

Trending Articles