|
|
| Turbocharge Your
Data Grid with a Span Query |
Level 200
DevForce Express |
Mar 26,
2007 |
New users
of DevForce sometimes experience
what they consider poor
performance in the loading
of data for datagrids.
There's a simple and profoundly
effective way to rocket
those data loads up to
warp speed, and you've
already guessed its essence
from the title of this
Tech Tip. But before we
give you the details, it's
important that you understand
a little bit about how
DevForce retrieves data
for related objects, and
why.
Suppose you need to fill
a datagrid with a collection
of Orders - lots of Orders.
You instantiate a DevForce
EntityList(Of T), mOrders,
to hold the Orders; and
a .NET BindingSource,
mOrdersBindingSource, to
keep the position pointer
in that list. You link
the two as follows: |
| |
C#:
mOrdersBindingSource.DataSource
= mOrders;
VB.NET:
mOrdersBindingSource.DataSource
= mOrders |
You use
a DevForce DataGridViewBindingManager
for a .NET DataGridView
grid (or the equivalent
BindingManager for a
Developers Express or
Infragistics grid), and
configure it to receive
its data from the
BindingSource:
|
| |
C#:
mOrdersDataGridViewBindingManager.BindingSource
= mOrdersBindingSource;
VB.NET:
mOrdersDataGridViewBindingManager.BindingSource
= mOrdersBindingSource
|
You then
retrieve the needed Orders
into an EntityList as
follows:
|
| |
C#:
mOrders.ReplaceRange(MyPersistenceManager.GetEntities(Of
Order)());
VB.NET:
mOrders.ReplaceRange(MyPersistenceManager.GetEntities(Of
Order)())
|
So far
so good...but at runtime,
your Orders grid seems
to take longer than it
should to fill with data.
What's the problem?
Almost invariably, the
source of the problem
will be that the Order
object has relation properties
--
like, say, Customer
and Shipper -- that connect
it to related entities;
and that those related
objects are referenced
in cells of the grid
rows. Now, relation
properties
are hardly a problem
in and of themselves:
in fact, they're quite
wonderful to work with,
making it extremely
easy and intuitive to
navigate
relationships in your
code, with statements
like this: |
| |
C#:
MyOrder.Customer.Agent.Contact.FullName;
VB.NET:
MyOrder.Customer.Agent.Contact.FullName
|
But DevForce
only loads the data for
relation properties on
demand by the client
application. When filling
a data grid, that demand
comes one Order at a
time. As each Order row
is loaded, it says, "And
by the way, can you also
get me the Customer that
placed me and the Shipper
that will ship me?" If
you're bringing a thousand
Orders into the grid,
that's 2000 separate
additional queries to
get those related Customers
and Shippers!
For many
of the things your application
will do, this default
behavior of "Just-In-Time" data
loading is perfect. It
keeps performance high,
and unnecessary data
retrievals to a minimum.
DevForce doesn't know,
when you call GetEntities(),
how you're going to use
the retrieved entities,
or which of the objects
related to the retrieved
entities, if any, you're
going to need. So it
only loads what you specifically
asked for, and waits
to see what you will
want to do next. A datagrid,
however, imposes special
demands by requiring
the UI to display a great
many objects at once.
In this circumstance,
leaving DevForce in the
dark about what related
objects you are going
to need results in poor
performance.
Span Queries to the
Rescue
Enter the span query: |
| |
C#:
EntityList mOrders
= new EntityList();
...
IdeaBlade.Persistence.EntityQuery
anOrderSpanQuery
=
new IdeaBlade.Persistence.EntityQuery(typeof(Order));
anOrderSpanQuery.AddSpan(EntityRelations.Order_Customer, EntityRelations.Customer_Agent,
EntityRelations.Agent_Contact);
anOrderSpanQuery.AddSpan(EntityRelations.Order_Shipper);
mOrders.ReplaceRange(mPersMgr.GetEntities(anOrderSpanQuery));
VB.NET:
Dim
mOrders As New EntityList(Of
Order)
...
Dim anOrderSpanQuery
As New IdeaBlade.Persistence.EntityQuery(GetType(Order))
anOrderSpanQuery.AddSpan(EntityRelations.Order_Customer,
_
EntityRelations.Customer_Agent,
_
EntityRelations.Agent_Contact)
anOrderSpanQuery.AddSpan(EntityRelations.Order_Shipper)
mOrders.ReplaceRange(mPersMgr.GetEntities(Of
order)(anOrderSpanQuery))
|
Like
an ordinary query, the
above query ultimately
returns a list of pointers
to objects of a single
type (in this case, Order)
in the DevForce local
cache. But in the process,
it makes sure that not
only those Orders, but
also the related Customer,
Agent, Contact, and Shipper
objects, are represented
in that cache. If they've
already been retrieved
by some prior data request,
GetEntities() simply
finds them in the cache;
if not, it goes out to
the back-end datasource
for them.
Now what happens
when it's time to fill
the
datagrid? DevForce still
must find the Contact
object referenced
in the statement: |
| |
C#:
MyOrder.Customer.Agent.Contact.FullName;
VB.NET:
MyOrder.Customer.Agent.Contact.FullName
|
But
no trips to the back-end
datasource are required.
The needed Customer object
is in the cache, as are
the related Agent and
Contact objects. DevForce
finds the targeted Contact
at local bus speed (fast!!!).
The grid fills quickly.
Best of Both Worlds
Now you have everything:
Just-In-Time data loading
as the default, so your
application doesn't waste
time retrieving data
it doesn't need; and
the ability to anticipate
the data it will need
and pre-fetch it in big
chunks. Have your cake
and eat it, too!
If your grid is going
to need to display a
really large number of
records, you might also
want to consider using
a Paged Query to retrieve
the data a specified
number of rows at a time.
Such paged queries, like
ordinary queries, can
also be defined with
spans so that you retrieve,
say, not only the next
100 Orders, but also
the Customers, Agents,
and Contacts related
to those Orders. And
if you really want to
tune for performance,
you can do all of the
above in conjunction
with one or more Asynchronous
Queries that retrieve
data on background threads
while your end user is
occupied with other business
in the user interface.
Your days of slow-loading
datagrids are over!
|
|
|
|
All
Tech Tips |