Generate sequencial number in a BO


Author
Message
Edhy Rijo
E
StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)
Group: StrataFrame Users
Posts: 2.4K, Visits: 23K
Hi All,

I have several form which will show a list with a sequential order number like 1,2,3,4, etc.  (See picture below).  I want this field LineOrder to be autogenerated with the next unused value when a new record is created, so if the last one used is 5, then when clicking New, the value will be 6.

I added the following code in the PaymentMethodBO.vb:

Private Sub PaymentMethod_SetDefaultValues()

     ' --- Calculate the next LineOrder number to be added as a default value for Me.LineOrder field.

     Me.MoveLast() ' This will move the record pointer to the last record, in this case the new one.

     Me.MovePrevious() ' Go back to the previous record which should be the last one.

     Dim nextLineOrder As Integer = 0

     nextLineOrder = Me.LineOrder + 1 ' Increment 1 to get the next default value.

     If nextLineOrder = 0 Then

          nextLineOrder = 1

     End If

     Me.MoveLast() ' Go back to the new record

     ' Set the default Value here

     Me.LineOrder = nextLineOrder

End Sub

So far this is working fine, but I would like to make sure this is the proper way to get this done when working with data in the BO.  I guess I may have another BO with a SELECT MAX() statement to get the last value used and then increment this value.  So I would appreciate any recommendation in this regard.

Thanks!Smile

Edhy Rijo

Greg McGuffey
Greg McGuffey
Strategic Support Team Member (3.4K reputation)
Group: Forum Members
Posts: 2K, Visits: 6.6K
This will work in a single user scenario, but if you have multiple users, especially if they are all adding new records, you'll get dups pretty quick. The BO is disconnected from the db, so if another user adds a new record after the user has filled the BO, they won't know about the newly added record from the other user. There are a few of solutions for this:



- If possible, just use an AutoIncrement field. The down side is that it is hard to have no holes (in some cases holes are a problem if the field is audited) and it will by a system number, not one that necessarily makes any sense in the context of the business domain.

- Query the db for the last LineOrder number and increment it. I've used this when it is very unlikely that two people are going to be adding records at the same time (in fact, it is a bad idea in these scenarios). Pretty much what you are doing, but if someone else adds new records while the user is in their BO, they'll see the changes.

- Use a table to hand out the LineOrder numbers. I.e. you query this table to get the next line order number. This is more complex because you have to deal with canceled adds, etc.



The long and sort of it is to use the DB to handle this, since the disconnected BO won't know about other users changes in a timely enough fashion (usually). I'm sure there are other ways to do this too, but thought I'd offer some ideas.
Edhy Rijo
E
StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)
Group: StrataFrame Users
Posts: 2.4K, Visits: 23K
Hi Greg,

This kind of data will be usually in use by a single user, since it is just the Line order for the records that will be shown as a lookup in a combo, so these forms will not be use by multiple users.  That's why I used the data in the BO to get the last one used, even though I think a better approach is to use the database with a view or SP to get the MAX() + 1.

Thanks for the explanation.

Edhy Rijo

Edhy Rijo
E
StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)
Group: StrataFrame Users
Posts: 2.4K, Visits: 23K
Hi Greg,

I ended up creating a function "GetNextLineOrder" in the BO which will query the table for the next available number.  Please see code below.

Only thing I could not figure out is how to pass the FieldName as a string instead of hard coding it in the Service_SetDefaultValues in order to make the call more generic since I will be using this code in several BO's.  Again, thanks for all your advice and pointing me in the right direction.

''' <summary>

''' Sets the default values for a new row

''' </summary>

''' <remarks></remarks>

Private Sub Service_SetDefaultValues()

' --- Calculate the next LineOrder number to be added as a default value for Me.LineOrder field.

     Me.LineOrder = Me.GetNextLineOrder("LineOrder", Me.TableName.ToString())

End Sub

 

''' <summary>

''' This function will generate a query to get the next available Line Order number.

''' </summary>

''' <param name="LineOrderFieldName"></param>

''' <param name="TableName"></param>

''' <returns></returns>

''' <remarks></remarks>

Private Function GetNextLineOrder(ByVal LineOrderFieldName As String, ByVal TableName As String) As Integer

     Dim sqlStringCommand As String

     sqlStringCommand = "SELECT MAX(" & LineOrderFieldName.ToString & ") + 1 FROM " & TableName.ToString

     Return CType(Me.ExecuteScalar(sqlStringCommand), Integer)

End Function

P.S.

I will add some TRY...ENDTRY error validation in case something happend in two months w00t

Edhy Rijo

Greg McGuffey
Greg McGuffey
Strategic Support Team Member (3.4K reputation)
Group: Forum Members
Posts: 2K, Visits: 6.6K
I might make a couple of suggestions:



- You don't need to use the ToString() on LineOrderFieldName or TableName. They are already a strings Wink

- Another way to get the same result as using the MAX function, is to use sorting and the top directive. I'm not sure which is faster, assuming that the LineOrderFieldName is indexed, but thought I'd throw out another idea. I.e. you sql would be something like:

"SELECT Top 1 (" & LineOrderFieldName & ") + 1 FROM " & TableName & " Order By " & LineOrderFieldName & " DESC"


- Use String.Format. It's a good habit to get into and it deals with strings more efficiently, as I recall. So now you might do the SQL String like:

String.Format("SELECT Top 1 [{0}] + 1 FROM {1} Order By [{0}] DESC", LineOrderFieldName, Me.TableName)


- When the BO is created there is an enum created that provides access to all the field names. It will be within the BO and have a name like MyBO.MyBOFieldNames. You could then just use the ToString() on the field (you'd replace MyBO with the actual name of your BO). I'm not sure this helps, but it does provide a strongly typed access to field names, so if you change the name, code won't compile and you can find the problem quicker:

String.Format("SELECT Top 1 [{0}] + 1 FROM {1} Order By [{0}] DESC", MyBO.MyBoFieldNames.LineOrderFieldName.ToString(), Me.TableName)


- This will fail if there are no LineOrder records found. I.e. the Select will return NULL as there is no data yet, and NULL + 1 is still NULL. So, you should either check the return for NULL (DbNull.Value) in you BO method or add to the SQL to handle it.



Good luck!
Edhy Rijo
E
StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)
Group: StrataFrame Users
Posts: 2.4K, Visits: 23K
Greg McGuffey (03/19/2008)

- When the BO is created there is an enum created that provides access to all the field names. It will be within the BO and have a name like MyBO.MyBOFieldNames. You could then just use the ToString() on the field (you'd replace MyBO with the actual name of your BO). I'm not sure this helps, but it does provide a strongly typed access to field names, so if you change the name, code won't compile and you can find the problem quicker:

String.Format("SELECT Top 1 [{0}] + 1 FROM {1} Order By [{0}] DESC", MyBO.MyBoFieldNames.LineOrderFieldName.ToString(), Me.TableName)

Hi Greg,

You definitely get me going Tongue

I made some modifications and checked the return value to foolproof the function.  I tested with a blank table and it worked perfectly.  I stick with the MAX() for simplicity, anyway those tables will not have many records anyway and they are properly indexed.  Here the new version:

Private Function GetNextLineOrder(ByVal LineOrderFieldName As String, ByVal TableName As String) As Integer

     Dim sqlStringCommand As String

     sqlStringCommand = String.Format("SELECT MAX( {0} ) + 1 FROM {1}", LineOrderFieldName, TableName)

     ' --- Test the returned result for DBNull value and if so return 1.

     Dim loValue As Object

     loValue = Me.ExecuteScalar(sqlStringCommand)

     If loValue Is DBNull.Value Then

          Return 1

     Else

          Return CType(loValue, System.Int16)

     End If

End Function

Of course I cheated a bit with the loValue code above which I copied from the BO designer file. Cool

For the Field Enums I only found a property Me.FieldEnums and frankly I could not found a way to use it to return the field name, but in this case it may no be necessary since the field name is the same in all tables "LineOrder", but it will be good to know how to get the field name from the field object Me.LineOrder.

Thanks again!

Edhy Rijo

Greg McGuffey
Greg McGuffey
Strategic Support Team Member (3.4K reputation)
Group: Forum Members
Posts: 2K, Visits: 6.6K
Looking good Edhy!



The FieldNames is an enum that is defined within the designer file of the BO. If you look in the Object Browser, find you BO, the next object in the list will be the BO. If the BO is named MyBO, then right after it will be an enum named MyBOFieldNames. If the BO is named YourBO, then right after it will be an enum named YourBOFieldNames.



I don't believe you can get the field name from the strongly typed property itself (i.e. you can't get the field name from Me.LineOrder).



Glad you're getting it going!
Trent Taylor
Trent Taylor
StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)
Group: StrataFrame Developers
Posts: 6.6K, Visits: 6.9K
Looks good...as long as you don't plan on two people hitting this table at the same time, then you could have an issue.  If you do not want to allow an auto-incrementing field to do this for you, then I would recommend creating a good ol' fashioned NextId table that is incremented with every query, this way if you have two, ten, or a hundred people hit this at the same time you will not have an overlapping of values.  Otherwise, your code really does look good.  The only thing I might mention would be the code you posted initially.  You were using MoveLast and the MovePrior.  You can do that, but you could do this in one line instead of two or three:

MyBo.MoveAbsolute(MyBo.Count-2)

But keep up the good work...it looks good! Smile

Edhy Rijo
E
StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)StrataFrame VIP (4.7K reputation)
Group: StrataFrame Users
Posts: 2.4K, Visits: 23K
Hi Trent,

For now these tables will be used initially in the application to setup some lookup values, so I am pretty sure only the Admin will have access to those forms, but I will enhance it to use an old fashioned NextID routine since there are a couple of those I need for Cash Receipt and Invoice.  I don't know if with SQL2005 you can have as many AutoIncrement fields as you want, but if not then a NextID routine will be needed to handle those numbers.

Thanks for the tip on MyBo.MoveAbsolute I'll keep it in mind since there are some data manipulations I need to do in this project but have not get there yet, so be ready for my questions BigGrin

Edhy Rijo

Trent Taylor
Trent Taylor
StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)
Group: StrataFrame Developers
Posts: 6.6K, Visits: 6.9K
I don't know if with SQL2005 you can have as many AutoIncrement fields as you want

You can have as many as you need and this is the recommended way to do things and is what we do.  However, if you need the old style of NextId, then you can do that too Smile

GO

Merge Selected

Merge into selected topic...



Merge into merge target...



Merge into a specific topic ID...




Similar Topics

Reading This Topic

Login

Explore
Messages
Mentions
Search