| Author |
Share Topic Topic Search Topic Options
|
davidklitzke
IdeaBlade
Joined: 14-Jun-2007
Posts: 715
|
Post Options
Quote Reply
Topic: WebForm-base app performance problems Posted: 05-Aug-2008 at 1:36pm |
Here are some introductory remarks on ITypedList:
One cannot add a dynamic property to a type: one can only tell a list that contains instances of that type to report (to whomever asks in the correct manner) that such and such property exists. This is done by writing the list so that it implements the .NET ITypedProperty interface, which mandates that the implementing list include a method called GetItemProperties(). Our lists, and some of the .NET lists, do implement ITypedList; but it is still up to the author of the list class to determine how GetItemProperties() shall decide what it returns.
When a consumer calls GetItemProperties() on one of our lists (BindableList<T>, or a subclass), the method searches a dictionary (again of our making) to see what properties it should tell the caller are available for binding on the contained type.
For example, suppose our ControlBindingManager designer asks the mEmployees entity list (by calling GetItemProperties() on it), "What properties are available on the Employee type for binding to?" mEmployees replies, "LastName, FirstName, BirthDate, and SalaryControlBackColor". LastName, FirstName, and BirthDate are properties that are defined in the Employee class, but SalaryControlBackColor is a property that your code added dynamically (perhaps by calling mEmployees.AddPropertyDescriptor()). AddPropertyDescriptor() didn't add the property to the Employee type directly -- that can't be done -- instead, it added a PropertyDescriptor representing SalaryControlBackColor to our dictionary of PropertyDescriptors. But anything reflecting directly against the Employee type for a list of properties will find nothing about a SalaryControlBackColor property.
When you code an expression like
emp.Manager.LastName,
.NET looks (directly) at the type (Employee) of which emp is an instance to see if that type contains a Manager property. If it does, then it looks at the Manager type to see if it contains a LastName property; and so on down the chain. If it looks at the Employee type for a "__Manager__LastName" property, it won't find it, because it doesn't exist on the Employee type: it only exists in our dictionary of PropertyDescriptors (where it is indeed associated with the Employee type). But .NET doesn't know anything about that list: it examines the Employee type directly.
You can find discussion of this in the section "Manipulating the List of PropertyDescriptors for a BindableList(of T)", in Chapter 7 of the Developers Guide. For a little more information, continuing reading the next section, "Global v. Private PropertyDescriptorList
Now that you understand ITypedList, look at the following proposal to add Nested Properties to the AspDataSource. This is a proposed feature enhancement:
Edited by davidklitzke - 05-Aug-2008 at 1:37pm
|
 |
kmg-pm
Newbie
Joined: 04-Jun-2008
Location: United States
Posts: 8
|
Post Options
Quote Reply
Posted: 04-Aug-2008 at 9:11am |
OK, using your IdeaBladeWeb project as an example...
You have an Orders GridView, ID="mOrdersGridView". Within it, a column called "EmployeeId" exits. You have a relationship setup linked to the Employee table called "SalesRep". So, what would be your suggested mechanism for displaying say... "SalesRep.LastName" instead of "EmployeeId"?
Currently I would create a property in the Orders class called SalesRepLastName, but this seems to defeat the point of linked objects.
|
 |
davidklitzke
IdeaBlade
Joined: 14-Jun-2007
Posts: 715
|
Post Options
Quote Reply
Posted: 01-Aug-2008 at 4:03pm |
I agree that scalability could become an issue later on. When that happens, you might consider selectively pruning the cache.
I don't understand your isue about linked object fields in a grid. Maybe, you could send me more details.
|
 |
kmg-pm
Newbie
Joined: 04-Jun-2008
Location: United States
Posts: 8
|
Post Options
Quote Reply
Posted: 01-Aug-2008 at 2:02pm |
Thanks David.
The EntityAdapterManager constructor code cleared things up for me. Implementing the Session state did make quite a difference in performance (of course, now that is makes sense). I am still a little worried about scalability though.
You suggested a StoredProcRdbQuery to help with the sort. I will play around with that, but did wonder about the best way to access linked object fields from a GridView? Is there something better than custom properties?
-Kevin
|
 |
davidklitzke
IdeaBlade
Joined: 14-Jun-2007
Posts: 715
|
Post Options
Quote Reply
Posted: 01-Aug-2008 at 1:01pm |
Here is the codefor the EntityAdapterManager i n deaBlade,Asp":
public EntityAdapterManager() {
if (HttpContext.Current != null)
this.PersistenceManager = System.Web.HttpContext.Current.Session["PersistenceManager"] as PersistenceManager;
}
Here is the code for Session_Start in Global_Asax:
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
Session["PersistenceManager"] = new PersistenceManager(true);
}
Edited by davidklitzke - 01-Aug-2008 at 1:02pm
|
 |
kmg-pm
Newbie
Joined: 04-Jun-2008
Location: United States
Posts: 8
|
Post Options
Quote Reply
Posted: 01-Aug-2008 at 10:48am |
Another question I have is, if I use the standard overrides for my EntityAdapterManager methods, how do I get/set the PersistenceManager that is stored in my Session state?
|
 |
davidklitzke
IdeaBlade
Joined: 14-Jun-2007
Posts: 715
|
Post Options
Quote Reply
Posted: 01-Aug-2008 at 10:48am |
I am not quite sure how you tested using the Session State, but I assume that you didn't use a new Persistence Manager on every call to GetEntities. Your current code basically throws away the cache from the last query
To get a better idea on where you are getting the performance hit, I would take performance measurements of the the time to collect the entities and do the sort.
Have you considered using a StoredProcRdbQuery?
|
 |
kmg-pm
Newbie
Joined: 04-Jun-2008
Location: United States
Posts: 8
|
Post Options
Quote Reply
Posted: 01-Aug-2008 at 8:09am |
David,
I decided to try and persist the PM in the SessionState just to see if it helped, and it appeared to have very little impact. In regards to your question about user load, even with one user it is very slow. You asked for a code sampe of one of my adapter managers, so here it is:
public class CGAdapterManager : EntityAdapterManager { public CGAdapterManager() { this.EntityType = typeof(CG); }
public override IEnumerable SelectEntities(IOrderedDictionary parameters, DataSourceSelectArguments pSelectArgs) { EntityQuery qry = new EntityQuery(this.EntityType); qry.AddClause("Deleted", EntityQueryOp.EQ, false); if (parameters["IsParent"] != null) qry.AddClause("ParentCGID", EntityQueryOp.IsNull);
PersistenceManager pm = new PersistenceManager(); EntityList<CG> list = pm.GetEntities<CG>(qry, QueryStrategy.Normal);
list.ApplySort("SortValue", ListSortDirection.Ascending, true);
return list.ToArray(); } }
As I mentioned before, many of our objects link to others many levels deep. To expose summary information on those objects, I have created DisplayText properties for displaying in the grid. I suspect it is this reaching in to other levels that is causing the performance hit. Another issue is likely the post-query sort. I would prefer to do this during the query, but it involves a complex sort on fields from different linked objects.
I think the reason my equivalent non-DevForce queries are so much faster is that they make use of JOINs to collect this summary info, and allows me to sort on it too. All of this in a stored proc.
Perhaps my methods for accessing these deeper level fields are the issue. Any help with that would be greatly appreciated.
Thanks,
Kevin
|
 |
davidklitzke
IdeaBlade
Joined: 14-Jun-2007
Posts: 715
|
Post Options
Quote Reply
Posted: 31-Jul-2008 at 1:15pm |
Greg,
By design, we use a single Persistence Manager and store the information in Session State. That means on every Page_Load all of the information about the Persistence Manager (and, in particular the PM cache) will be retrieved from Session State). You are correct that storing the PM Session State could hinder scalability, but without some way of retaining the information about the cache from one Page_Load to the next, the cache is useless.
How are you you accessing the information in the Peristent Manager cache from one Page Load to the next. If you are not using Session State, I suspect that you are not using the cache at all.
If you are not using Session State, how are you initiializing the PM from one Page_Load to the next. Creating or reinitializing a Perrsistence Manager is not necessarily a speedy operation. Maybe the slowness is due to PM reinitialization and not the cache.
I would like to know if you have a performance problem mostly with many users, or do you see poor performance even with a single user.
The fact that you are getting decent performance with Object Data Source and SQL Data Source is interesting I feel that with the right changes, we should be able to get performance that is at least as good as these two other implmentations.
Could I see the constructor for one of your Adapter Managers?
Do you have any measurements on what is really slow? For example. is the Page_Load very slow, or is it Select_Entities?
David
|
 |
kmg-pm
Newbie
Joined: 04-Jun-2008
Location: United States
Posts: 8
|
Post Options
Quote Reply
Posted: 31-Jul-2008 at 10:03am |
We have a WebForm based web application that was developed from the start using DevForce Enterprise. Our basic strategy was to follow the example used in the DF ASP.NET tutorial, binding to grids using AspDataSource and corresponding custom EntityAdapterManager classes. Each page instantiates its own PersistenceManager (we thought the Session method would hinder scalability).
At first, performance was just fine. As we progressed over time, things got significantly slower. Our classes have evolved, linking more to other classes, but our dataset sizes have not really grown. Basically performance has degraded so that it is unacceptable. I have looked through these forums for any kind of information to generally improve performance on the web, but have not found anything.
What kind of things can be done to optimize performance? On a side note, performance with the same data using SqlDataSource or non-DF ObjectDataSource is good, but we'd like to try and use DF.
If anyone could provide help and/or suggestions, it would be greatly appreciated!!
-Kevin
|
 |