Performance tip #1: Use data classes

General Discussions
Best Practices
Examples

Performance tip #1: Use data classes

Postby Christian Estrup » Mon Feb 15, 2010 3:00 pm

This is the single most valuable optimization tip for 99% of poor-performance cases we’ve seen!
What many developers overlook is that the ‘normal’ entity classes – e.g. IProduct and IInvoice – are essentially proxy classes, in themselves containing no data except a simple reference. This means that every single reading or writing of properties results in a round-trip! For example, reading the Name property of an IProduct object is essentially a wrapper around the SOAP call Product_GetName. This is obviously extremely inefficient when you need to read or write many multiple properties on multiple objects.

Enter data classes:

- All classes have a data class equivalent – for example, the data class equivalent of the IProduct class is the IProductData class
- Data classes expose the exact same properties and methods as their non-data equivalents
- Reading and writing properties on a data object never results in a round-trip
- The price: You must explicitly ‘save’ changes on data objects

Data classes can be used for both reading, creating and updating entities:
- To read a single entity, use GetData()
- To read multiple entities, use GetDataArray()
- To update from a single entity, use UpdateFromData()
- To update from multiple entity, use UpdateFromDataArray()
- To create a single entity, use CreateFromData()
- To create multiple entities, use CreateFromDataArray()

In addition to the huge performance gain, a further advantage of using data classes is that they add a somewhat more transactional level to writing data – since the e-conomic API generally works transactionally on a per-round-trip basis.

There are, however, also a few ‘gotchas’ when using data arrays:
- Whether you’re reading, creating or updating data, you should limit each ‘bulk’ – array – of data to a maximum of 500 or 1,000 entities. Otherwise, you’ll easily run into client-side timeouts.
- Data classes only save you round-trips on simple properties and basic object reference properties. For example, if you read myProductData.ProductGroup.Name, this will result in a round-trip (calling the SOAP method ProductGroup_GetName). For some background and tips for working around this, you can read this blog post.
Christian Estrup
Chief Architect

Image
Online accounting
User avatar
Christian Estrup
 
Posts: 206
Joined: Tue Jun 09, 2009 6:37 pm

Re: Performance tip #1: Use data classes

Postby sandeep » Wed Jul 27, 2011 9:10 am

Hi Christian,

I am fetching data like below:

IInvoice[] invoices = this._Session.Invoice.GetAll();
IInvoiceData[] invoiceData = this._Session.InvoiceData.GetDataArray(invoices);

it likely that there are more than 1000 records of invoices for any user. So how can fetch data in chunks of 500 or 1000. can you provide any code sample to fetch data in chunk.
-
sandeep
 
Posts: 1
Joined: Wed Jul 27, 2011 9:01 am

Re: Performance tip #1: Use data classes

Postby Carsten Larsen » Thu Aug 04, 2011 10:24 am

Hi sandeep

Here is a function that collects all invoices through several batches. You can specify the batch size as a function parameter. So if you have 1187 invoices and the batch size is set to 500, then all 1187 invoices is returned from the function. But internally in the function it retrieves 500 + 500 + 187 invoices (3 round trips).

public IInvoiceData[] GetAllInvoicesUsingBatches(EconomicSession session, int batchSize)
{
IInvoice[] allInvoiceHandles = session.Invoice.GetAll();
IInvoiceData[] allInvoiceData = new IInvoiceData[allInvoiceHandles.Length];

// calculate number of batch runs
int iterations = (allInvoiceHandles.Length + batchSize - 1) / batchSize;

IInvoice[] batchHandles = new IInvoice[batchSize];
IInvoiceData[] batchData = new IInvoiceData[batchSize];

// run each batch and add to result (allInvoiceData)
for (int iteration = 0; iteration < iterations; iteration++)
{
int currentBatchSize = batchSize;
// special case: last batch might be smaller than normal batch size
if (iteration == iterations - 1)
{
currentBatchSize = allInvoiceHandles.Length % batchSize;
}
// fill this batch with data
Array.Copy(allInvoiceHandles, iteration * batchSize, batchHandles, 0, currentBatchSize);
batchData = session.InvoiceData.GetDataArray(batchHandles);
// add batch to final result
Array.Copy(batchData, 0, allInvoiceData, iteration * batchSize, currentBatchSize);
}

return allInvoiceData;
}

The function can be used like this:

int batchSize = 500;
IInvoiceData[] allInvoiceData = GetAllInvoicesUsingBatches(session, batchSize);

Carsten, e-conomic API team
-
Carsten Larsen
 
Posts: 5
Joined: Mon Jan 24, 2011 11:37 am


Return to Miscellaneous