Print Page | Close Window

Any idea about speeding up the load time of a form?

Printed From: IdeaBlade
Category: DevForce
Forum Name: DevForce Classic
Forum Discription: For .NET 2.0
URL: http://www.ideablade.com/forum/forum_posts.asp?TID=1056
Printed Date: 23-Apr-2025 at 10:20pm


Topic: Any idea about speeding up the load time of a form?
Posted By: HFloyd
Subject: Any idea about speeding up the load time of a form?
Date Posted: 09-Jan-2009 at 12:04pm
In my application, I have noticed that certain forms take quite awhile to display the first time during an application session, but then act responsively when the same form is requested later in the session. Of course, after the application is shut down and re-opened, it again takes a very long time to display the form.

For instance, I have a "Transactions" Form, which takes an unimaginably long time to display (nearly 10 minutes), and when I check the debuglog, it lists 2000 lines of database queries:

RdbKey: [default] Sql: select * from "dbo"."FinancialTransaction"
RdbKey: [default] Sql: select * from "dbo"."TransactionLineItem" where ((1 = 0))
RdbKey: [default] Sql: select * from "dbo"."TransactionLineItem" where ("dbo"."TransactionLineItem"."TransactionREF" in (?)) Params: v0=00165662-a9be-4900-84eb-3b2ee391349e
RdbKey: [default] Sql: select * from "dbo"."EventTicketSale" where ((1 = 0))
RdbKey: [default] Sql: select * from "dbo"."EventTicketSale" where ("dbo"."EventTicketSale"."TransactionLineItemREF" in (?)) Params: v0=56e4a6d4-a73f-4a1d-8eb8-459b098f6877
RdbKey: [default] Sql: select * from "dbo"."Payment" where ((1 = 0))
RdbKey: [default] Sql: select * from "dbo"."Payment" where ("dbo"."Payment"."TransactionREF" in (?)) Params: v0=00165662-a9be-4900-84eb-3b2ee391349e
RdbKey: [default] Sql: select * from "dbo"."HouseholdBusiness" where ((1 = 0))
RdbKey: [default] Sql: select * from "dbo"."HouseholdBusiness" where ("dbo"."HouseholdBusiness"."HouseholdBusinessGUID"=?) Params: v0=dfecf0d1-84f8-470a-97d6-e1c6ae121c07
RdbKey: [default] Sql: select * from "dbo"."TransactionLineItem" where ("dbo"."TransactionLineItem"."TransactionREF" in (?)) Params: v0=8253ad0f-a3a0-44a3-ae56-7214fd358cfb
RdbKey: [default] Sql: select * from "dbo"."TransactionLineItem" where ("dbo"."TransactionLineItem"."TransactionREF" in (?)) Params: v0=ffa1380b-c33f-46ea-8d28-4a528865e113
RdbKey: [default] Sql: select * from "dbo"."HouseholdBusiness" where ("dbo"."HouseholdBusiness"."HouseholdBusinessGUID"=?) Params: v0=48f53d40-1eff-4a5f-9788-18e9246dc5f9
RdbKey: [default] Sql: select * from "dbo"."TransactionLineItem" where ("dbo"."TransactionLineItem"."TransactionREF" in (?)) Params: v0=0018c8c4-b615-4c6e-9120-167fd8dc369c
RdbKey: [default] Sql: select * from "dbo"."HouseholdBusiness" where ("dbo"."HouseholdBusiness"."HouseholdBusinessGUID"=?) Params: v0=bcddfc9d-6fc1-4a62-a72c-ca92f7e85219


Does anyone have any insight into this? Specifically, I would love to know if there is some way I can speed up the form display and possible "pre-load" the form (on a different thread) when the application starts, so when the user requests the form, it displays quickly.

Right now I have this code running in the Shell application (on app start):
(entityTypes is a list of a few of the entity types in my application which seem to be the slow-loading form entities)


/// <summary>Get all Entities of Types in a list into the PM asynchronously</summary>
        /// <param name="entityType">The list of Types to get entities of</param>
        public static void GetDataAsync(IEnumerable<Type> entityTypes)
        {
            string TypeName;
            string Token;

            foreach (Type type in entityTypes)
            {
                TypeName = type.Name;
                Token = TypeName + "_Complete";

                RdbQuery ASQuery = new RdbQuery(type);

                MainPm.Manager.GetEntitiesAsync(ASQuery, QueryStrategy.Normal, Token);

                DebugFns.WriteLine("FI_CODE: GetDataAsync() " + TypeName + " Completed (" + Token + ")");
            }
        }


Is there something else I should be doing?

Thanks in advance!

Heather



Replies:
Posted By: davidklitzke
Date Posted: 12-Jan-2009 at 4:31pm
Heather,
 
Just getting the data asynchronously may not help very much if your first asynchronous request gets a lot of data from the database (or too little data).  Try to get enough data on your first request so that you can make an initial display of the form, then in your background thread, you can ask for more data.
 
I would also consider using paged queries, and a paged form.
 
David


Posted By: HFloyd
Date Posted: 14-Jan-2009 at 6:08pm
Thanks for the ideas, David. I am investigating the "paged queries".

How I have it set up now is that on application open, I have async queries running that pull data from all the tables into the cache. The user interface of the application appears in a reasonable amount of time for "application boot" (the initial "home" screen doesn't contain any data for display), but when the user then clicks to one of the main forms (which holds all the people and addresses, etc. in the database (11K records), it takes a very long time to load, or seems to hang completely.

The user might want to look up any person in the system, so I'm not sure about how paging would work (they use a lookup combo box to select a person to "jump" to, generally, though the form also has a DataNavigator control).

I have looked at the sample code on Paging: http://www.ideablade.com/forum/forum_posts.asp?TID=40 - http://www.ideablade.com/forum/forum_posts.asp?TID=40 but am unclear about the best way to implement it in my user interface....

Currently this is how I set up the form:

private void HouseholdsBusinesses_Load(object sender, EventArgs e)
        {
            //Data
            base.InitalizePM();
            ConfigureBindingSources();
            LoadData();
           ....other basic form setup code...
}

        public override void ConfigureBindingSources()
        {
            //Binding Sources - Main
            this.bsMainEntity.DataSource = mListMainEntity;
            this.bsMainFilter.DataSource = mListFilterLookup;

            //Binding Sources - Lookup Lists
            this.bsUseSalutationTypes.DataSource = mListUseSalutationTypes;
            this.bsUseFullNamesTypes.DataSource = mListUseFullnameTypes;
            this.bsHBType.DataSource = mListHBTypes;
            this.bsReferredByPeople.DataSource = mListReferredByPeople;
            this.bsRelationshipSourceType.DataSource = mListRelationshipSource;
            this.bsCountries.DataSource = mListCountries;
            this.bsAuctionYears.DataSource = mListAuctionYears;
            this.bsContactMethodTypes.DataSource = mListContactMethodTypes;
           
            //Data Navigator
            this.MainDataNavigator.DataSource = bsMainEntity;
        }

public override void LoadData()
        {
            //Main Lists
            mListMainEntity.ReplaceRange(mPersMgr.GetEntities<HouseholdBusiness>());
            mListMainEntity.ApplySort(
                               EntityPropertyDescriptors.HouseholdBusiness.DefaultSort,
                               ListSortDirection.Ascending);
 
            mListFilterLookup.ReplaceRange(mPersMgr.GetEntities<HouseholdBusiness>());
            mListFilterLookup.ApplySort(
                               EntityPropertyDescriptors.HouseholdBusiness.DefaultSort,
                               ListSortDirection.Ascending);
 
            //Lookup Lists
            mListHBTypes.ReplaceRange(mPersMgr.GetEntities<HBType>());
           
            mListReferredByPeople.ReplaceRange(mPersMgr.GetEntities<Person>());
            mListReferredByPeople.ApplySort(
                               EntityPropertyDescriptors.Person.DefaultSort,
                               ListSortDirection.Ascending);

            mListRelationshipSource.ReplaceRange(mPersMgr.GetEntities<RelationshipSource>());
            mListRelationshipSource.ApplySort(
                               EntityPropertyDescriptors.RelationshipSource.DefaultSort,
                               ListSortDirection.Ascending);

            mListCountries.ReplaceRange(mPersMgr.GetEntities<Country>());
            mListCountries.ApplySort(
                               EntityPropertyDescriptors.Country.DefaultSort,
                               ListSortDirection.Ascending);

            mListAuctionYears.ReplaceRange(mPersMgr.GetEntities<AuctionYear>());
            mListAuctionYears.ApplySort(
                               EntityPropertyDescriptors.AuctionYear.DefaultSort,
                               ListSortDirection.Ascending);

            mListContactMethodTypes.ReplaceRange(mPersMgr.GetEntities<ContactMethodType>());
            mListContactMethodTypes.ApplySort(
                               EntityPropertyDescriptors.ContactMethodType.DefaultSort,
                               ListSortDirection.Ascending);

            mListUseSalutationTypes.ReplaceRange(mPersMgr.GetEntities<UseSalutationType>());
            mListUseSalutationTypes.ApplySort(
                               EntityPropertyDescriptors.UseSalutationType.USTOrderNum,
                               ListSortDirection.Ascending);

            mListUseFullnameTypes.ReplaceRange(mPersMgr.GetEntities<UseFullNamesType>());
            mListUseFullnameTypes.ApplySort(
                               EntityPropertyDescriptors.UseFullNamesType.UFTOrderNum,
                               ListSortDirection.Ascending);
        }


I tried to alter LoadData() like this:

    //Main Lists
            // Query orders in ascending HBName  sequence.
            RdbQuery aQuery = new RdbQuery(typeof(HouseholdBusiness));
            aQuery.AddOrderBy(HouseholdBusiness.HBNameEntityColumn, ListSortDirection.Ascending);

            // Build a paged query to retrieve 10 items at a time.
            PagedRdbQuery pagedQuery = new PagedRdbQuery(aQuery);
            pagedQuery.PageSize = 10;

            // Specify whether the last item of the page will be first item on next page.
            pagedQuery.PagesShouldOverlap = false;
                       
        while (true)
        {
                mListMainEntity.ReplaceRange(mPersMgr.GetEntities<HouseholdBusiness>(pagedQuery));
                DebugFns.WriteLine("Current page = " + pagedQuery.CurrentPage.ToString());
                DebugFns.WriteLine("Count for this page = " + mListMainEntity.Count.ToString());

                if (pagedQuery.IsLastPage) break;

                // Advance the query.
                pagedQuery.NextPage();
        }
...


but it took just as long (or possibly longer...) with no quicker form display, and once the form DID display - it was totally empty of data. I must be missing something very basic here...

I also was experiementing with running the query async...

mListMainEntity.ReplaceRange(mPersMgr.GetEntitiesAsync<HouseholdBusiness>(aQuery, QueryStrategy.Normal, "HB_Paged"));

but that won't compile...

I don't mind if the records take some time to load into the system, but I'd really like to have the form display with SOMETHING as soon as possible, and still have all the data loading in for access.

If someone could point out how I am not understanding this, or provide some code samples I would be grateful.

Thanks,
Heather




Posted By: davidklitzke
Date Posted: 15-Jan-2009 at 4:43pm
Heather,
 
Continue to work on thr paging example.  I am sire that you will eventually "get it".
 
Also, work through the Asynch Query examples in the Tutorials until you understand why this helps performance.
 
Maybe, your problem is that you just have too much data.  Is there any way to make the amount of data smaller.  For example, can you have your user choose (to begin with) "This Year's Customers" and "Last Year's Customers", or "Customers from New York" and "Customers from New Jersey".  Look at some examples of our QBE Control where an initial control narrows the number of items that will be included.
 
You say that "The user might want to look up any person in the system, so I'm not sure about how paging would work".
 
You have a choice.  You can try to display a list of all the customers and then have your user "page" or "scroll" that list, or you can start with a "search" function (or both).  If you don't have to build a large list and can just "search", that is the fastest, but probably, you want your user to be able to page though a large list.
 
Another advanced techniaue you might consider is to use a "Dynamic Entity".  For example, get just the First Name and Last Name of each customer, and construct a Dynamic Entity that just has these two columns.  and so you would just be retrieving two columns of each row.  That would make the retrieval from the database faster. Display a list of these Dynamic Entities.  When the user clicks on the Customer, you could retrieve the record with all columns.
 
David


Posted By: HFloyd
Date Posted: 02-Feb-2009 at 7:43pm
Hi David.

As per Greg's instructions - http://www.ideablade.com/forum/forum_posts.asp?TID=261
I have sent a request to support for the QBE code...

Heather


Posted By: HFloyd
Date Posted: 30-Jun-2009 at 1:32pm
Believe it or not, I am still working on improving the load time of my forms.

I have been able to make some progress - I added a quick "Search" box to the top of the form, so the underlying data is not loaded until the user types in part of a name to search on. After clicking a "Get Records" button, an RDB query retrieves only the few records which match that search term, then the user can click through that smaller result-set. This is working pretty well, and the record retrieval is fairly quick.

However, I noticed that some of the combo boxes on my form still require a large number of records to be retrieved. For instance, I have an Entity called "Household" and that is related to an entity called "Person" in a 1-many relationship. If I am on the "people" form, there is a combo box indicating which Household that person belongs to. The box needs to have a list of all the available households, and should be displaying the current linked household name in the combo box on the form.

I found that as long as I am calling the database to fill a list for that combo-box, the load time of the form is still very long. On Form_Load I have it pull in the lookup list data:

//Lookup Lists
             mListInvolvementType.ReplaceRange(mPersMgr.GetEntities<InvolvementType>());
             mListInvolvementType.ApplySort(
                                EntityPropertyDescriptors.InvolvementType.DefaultSort,
                                ListSortDirection.Ascending);

             mListHouseholdBusiness.ReplaceRange(mPersMgr.GetEntities<HouseholdBusiness>());
             mListHouseholdBusiness.ApplySort(
                                EntityPropertyDescriptors.HouseholdBusiness.DefaultSort,
                                ListSortDirection.Ascending);



Since the Household lookup list only needs a few columns - the GUID and some display properties, I thought I would create a separate entity type which would only get those few necessary columns to be used as the source of the look-up list, and load those instead:

             ...
             mListHouseholdBusiness.ReplaceRange(mPersMgr.GetEntities<HouseholdBusiness_Lookup>());
             mListHouseholdBusiness.ApplySort(
                                EntityPropertyDescriptors.HouseholdBusiness_Lookup.DefaultSort,
                                ListSortDirection.Ascending);


Which does somewhat reduce the load time, but the combo-box on the form does not display the currently linked Household. I shows as blank, witht he ability to select from the "HouseholdBusiness_Lookup" list.

Perhaps it is treating the relationship between "Person" and "HouseholdBusiness_Lookup" as different from the relationship between "Person" and "HouseholdBusiness" because they are different entities - even though the same database relationship is used to define them?

Am I going about this wrong - or will I need to load int he regular "HouseholdBusiness" entity to fill the look-up list?



Posted By: GregD
Date Posted: 02-Jul-2009 at 12:46pm
Try displaying the HouseholdBusiness_Lookup relation property of the Person in the ComboBox, instead of the Household. Then, in a custom setter for HouseholdBusiness_Lookup, also set the Household.



Posted By: HFloyd
Date Posted: 02-Jul-2009 at 3:04pm
Thanks for your reply, Greg.

I actually decided that the overhead to load in all the possible objects (even if they were "smaller") was still too much, so I am removing the drop-down from the main form and instead having a button to "Change..." which will bring up a pop-up form to choose the new Household. This will limit the data loading to happen only when requested by the user (which, in reality, won't be that often).

There are really only 2 entities which are this unwieldy, so implementing the dialog box shouldn't be too onerous.


Heather



Print Page | Close Window