Please help and suggest best practice for follow scenario
Customer
Document
DocumentLines
Item
Customer has Association to Document
Document has Association to DocumentLines
Item has Association to DocumentLines
How to calculate Totals on Customer, Document and Item
Image that there are about 200 Customers, 50 Items, every customer has daily (200 to 250 days per year) one Document (invoice or deliverynote or cancelationnote) every document has from 5 to 10 DocumentLines and we want document history for 5 year. It means we will have about 2.000.000 to 2.500.000 DocumentLines.
We have closed this ticket because another page addresses its subject:
Specific scenarios with calculated properties defined via the PersistentAlias attribute where the performance can be improved
Hi Panagiotis,
To calculate totals you can create read-only properties in your objects, marked with the PersistentAlias attribute, and specify the total expression in it. For example:
Public Class DocumentLine Inherits XPBaseObject ... <PersistentAlias("Quantity*Price")> _ Public ReadOnly Property Amount() As Decimal Get Return Convert.ToDecimal(EvaluateAlias("Amount")) End Get End Property End Class
Public Class Document Inherits XPBaseObject ... <PersistentAlias("DocumentLines.Sum(Amount)")> _ Public ReadOnly Property Amount() As Decimal Get Return Convert.ToDecimal(EvaluateAlias("Amount")) End Get End Property End Class
Use similar code in the Item and Customer classes.
Please let us know if this solution meets your requirements.
Thanks,
Michael.
Hi Michael
I have modify my classes according to yours suggestion. There is new example with demo data( only 10 Customers, and 9 Items). Please try edit Document (Invoice) and add new row to DocumentLines or change a customer.
The speed of LookupEditors for Item or Customer is very slow !!!.
If you can suggest solution.
Please down not point to example E1477 or E305 I have see them.
Hi Panagiotis,
Please accept our apologies for the delay in responding. We couldn't find an immediate answer or resolution. Please bear with us. We will inform you as soon as an answer is found.
Thanks,
Michael.
Hi Panagiotis,
Bad performance is caused by the fact that XPO cannot generate a single SQL query for nested calculated properties. So, it retrieves all referenced DocumentLine objects, and calculates the aggregate function on the client side. I have registered a suggestion to support this scenario in the future: Optimize SQL query generation for nested persistent alias properties. Please track it to be notified when its status is changed.
Currently, you can improve performance, only by executing an explicit SQL query, like this:
SELECT Sum(dl.Price*dl.Quantity) AS s FROM (Customer AS c INNER JOIN Document AS d ON c.Oid = d.Customer) INNER JOIN DocumentLine AS dl ON d.Oid = dl.Document WHERE c.Oid = @p;
See also:
Direct SQL Queries.
Thanks,
Michael.
Hi Michael
Thank you for your answer.
I have modify my classes according using example E305. (How to: Calculate a Property Value Based on Values from a Detail Collection.) So the speed of LookupEditors is solved, but there is small problem, Please try edit Document and add new row to DocumentLines choose Item and move to field Quantity. The speed is very slow. How to resolve this. I want mention that this after choosing the item there are other jobs to be done (as find last price of the customer or assign price from agreement, select vat category and etc). Please suggest solution.
And one small suggestion. The XPO object should create index for field the customer in the table Document and for field Document and Item in table the DocumentLines
(I have running applications but I am afraid as data are growing and I want prevent bottleneck in the future so this is simplified sample.)
Thank you in advance.
Hi Panagiotis,
A new DocumentLine object is created when any cell value is set in the grid's new item row. When you set the Item property, it should update its associated collection of DocumentLines. To add a newly created DocumentLine to it, XPO has to load all objects in this collection (see suggestion XPO should not automatically pre-load an associated collection when I only need to insert a new object into it). What is worse, all associated Document objects are also loaded, because the DocumentLine object has a reference to Document. Retrieving Documents generates a lot of queries, because they are retrieved by their keys. You can avoid automatic loading of Document objects by enabling delayed loading for the DocumentLine.Document property. Please refer to the Delayed Loading help topic.
As for database indices, XPO does not automatically create them, because it is impossible to determine when they are necessary. This optimization should be done based on real life scenarios and tests. You can use the Indexed attribute to instruct XPO to create a database index.
Thanks,
Michael.
Thank you for your answer.
Please suggest how to calculate Item.DocumentLinesTotal (QuantityDebit, QuantityDebit …) if I will remove Association (<Association("Item-DocumentLine")>) from class Item
Take in account that in the class Item there are more properties that should be calculated online so the LookupEditors will have correct balances
Public Class Item
Inherits XPBaseObject
Public Sub New(ByVal session As Session)
MyBase.New(session)
End Sub
Public Overrides Sub AfterConstruction()
MyBase.AfterConstruction()
End Sub
<Key(True)>
Public Property Oid() As Integer
Public Property Description() As String
'Uncomented Line
'<Association("Item-DocumentLine")>
Public ReadOnly Property DocumentLines() As XPCollection(Of DocumentLine)
Get
Return GetCollection(Of DocumentLine)("DocumentLines")
End Get
End Property
Public Property DocumentLinesTotal() As Nullable(Of Decimal)
Public Property QuantityDebit() As Nullable(Of Decimal)
Public Property QuantityCredit() As Nullable(Of Decimal)
Public Property AmountDebit() As Nullable(Of Decimal)
Public Property AmountCredit() As Nullable(Of Decimal)
Public Overrides Function ToString() As String
Return Me.Description
End Function
End Class
Thanks
Panos
Hi Panos,
If you want to remove the association, you should rewrite your DocumentLinesTotal property to use an explicitly created collection, filtered to contain related objects.
Dim total As Decimal = 0 Dim documentLines As New XPCollection(Of DocumentLine)(Session, New BinaryOperator("Item", This)) For Each c As DocumentLine In documentLines total += c.Amount Next c m_documentLinesTotal = total
I'm looking forward to hearing from you.
Thanks,
Michael.