Performance issues with 3.6.1?

Performance issues with 3.6.1?

Old forum URL: forums.lhotka.net/forums/t/6627.aspx


ajj3085 posted on Wednesday, March 18, 2009

Hi,

I'm using Csla 3.6.1.  I've switched my criteria objects for some searches to inherit BusinessBase, so that I can use validation rules to make sure at least one criteria is entered.  The classes also use the PropertyInfo class to store properties.

My users are complaining of performance problems when typing in search criteria.  When the application first starts, everything is snappy.  As they use it more, it seems to get slower and slower... to the point were just typing in a System.Windows.Forms.TextBox takes about 4 seconds before all the characters appear.  I have the binding setup for OnPropertyChanged instead of OnValidation, but this has never been a problem before.  Also, the rule check is just a serious of string.IsNullOrEmpty calls..

Each form is recreated each time, as is the criteria.  The only static members are the criteria's PropertyInfo.

Any ideas?

rfcdejong replied on Wednesday, March 18, 2009

Maybe you hooked events and they aren't being unhooked?

I would use an profiler and click myself alot. If i can repoduce how and when to let the performance slack i analyse the profiled output. The profiler from RedGate is my favorite :)

Else use your business object in a small frontend program and see if it goes slower over time, or create a new small business object and double check if it isn't CSLA, but my guess is that u must be doing something wrong (and i hope hehe, sorry)

ajj3085 replied on Monday, March 23, 2009

So it seems that changing all textbox bindings to OnValidation from OnPropertyChanged resolves the issue.

I'm not sure why though.. this was working fine for some time.    Also, it only happens after using the application for some length of time.  15 - 20 minutes of use. 

rfcdejong replied on Monday, March 23, 2009

omg, we were going to use OnPropertyChanged on each control. At least for the first modules to see if it does any harm.

Are u using Winforms or WPF?

tetranz replied on Monday, March 23, 2009

Others may disagree but I don't think there is a good reason to do WinForms binding OnPropertyChanged for a textbox. I use OnPropertyChanged for checkboxes, dropdown lists etc, i.e, "instant change" type controls but I always leave it at OnValidation for textboxes. Checkboxes are especially confusing for the user who is expecting to see something change when they click it. Textboxes are not so bad. "Type in it and then hit tab" seems to work for most users.

Don't forget, when WinForms binding updates a property it refreshes EVERY control. Do you really want every control (including all items in child lists) refreshing on every keystroke?

That said, I don't know why an application would go well and then slow down. It does sound like events being repeatedly hooked but not unhooked.

RockfordLhotka replied on Monday, March 23, 2009

While this may be a CSLA issue, I don't think it is. Andy isn't the only one using OnPropertyChanged, but this is the first I've heard of an issue like this.

Someone earlier mentioned that it sounded like an event leak - and I agree - it does sound like something is getting hooked or memory consumed, or something, on each validation.

Andy, can you try this on an object that has either no rules, or very simple rules (like StringRequired) and see if it bogs down? That'd tend to indicate an issue with CSLA leaking. Otherwise, it is probably something in one of your rules where code is being triggered that hooks an event or consumes some other resource on each invocation.

ajj3085 replied on Tuesday, March 24, 2009

Rocky, the sole rule method is a glorified StringRequired situation.  Here's the entire class.. unfortunately I'm not sure I have time to look into this further at the moment.  I couldn't see anything that looks like it should cause problems.

    [Serializable]
    public sealed class ContactListSearchCriteria :
        BusinessBase<ContactListSearchCriteria>,
        ICriteria
    {
        #region Fields

        private static readonly PropertyInfo<string> FirstNameProperty =
            RegisterProperty<string>( new PropertyInfo<string>( "FirstName" ) );
        private static readonly PropertyInfo<string> CompanyProperty =
            RegisterProperty<string>( new PropertyInfo<string>( "Company" ) );
        private static readonly PropertyInfo<string> LastNameProperty =
            RegisterProperty<string>( new PropertyInfo<string>( "LastName" ) );
        private static readonly PropertyInfo<string> PostalCodeProperty =
            RegisterProperty<string>( new PropertyInfo<string>( "PostalCode" ) );
        private static readonly PropertyInfo<string> CountryProperty =
            RegisterProperty<string>( new PropertyInfo<string>( "Country" ) );
        private static readonly PropertyInfo<string> TitleProperty =
            RegisterProperty<string>( new PropertyInfo<string>( "Title" ) );
        private static readonly PropertyInfo<string> SalutationProperty =
            RegisterProperty<string>( new PropertyInfo<string>( "Salutation" ) );
        private static readonly PropertyInfo<string> CityProperty =
            RegisterProperty<string>( new PropertyInfo<string>( "City" ) );
        private static readonly PropertyInfo<string> AddressLine1Property =
            RegisterProperty<string>( new PropertyInfo<string>( "AddressLine1" ) );
        private static readonly PropertyInfo<string> AddressLine2Property =
            RegisterProperty<string>( new PropertyInfo<string>( "AddressLine2" ) );
        private static readonly PropertyInfo<string> AddressLine3Property =
            RegisterProperty<string>( new PropertyInfo<string>( "AddressLine3" ) );
        private static readonly PropertyInfo<string> StateProperty =
            RegisterProperty<string>( new PropertyInfo<string>( "State" ) );
        private static readonly PropertyInfo<string> EmailProperty =
            RegisterProperty<string>( new PropertyInfo<string>( "Email" ) );
        private static readonly PropertyInfo<bool> DummyProperty =
            RegisterProperty<bool>( new PropertyInfo<bool>( "Dummy" ) );

        #endregion
       
        #region Properties
       
        /// <summary>Value to search
        /// for with in the contacts
        /// first name.</summary>
        /// <value>A <see cref="String"/>.</value>
        public string FirstName {
            get { return GetProperty( FirstNameProperty ); }
            set { SetProperty<string>( FirstNameProperty, value ); }
        }

        /// <summary>Value to
        /// search for within the
        /// contacts company field.</summary>
        /// <value>A <see cref="String"/>.</value>
        public string Company {
            get { return GetProperty( CompanyProperty ); }
            set { SetProperty<string>( CompanyProperty, value ); }
        }

        /// <summary>Value to search
        /// for with in the contacts
        /// last name.</summary>
        /// <value>A <see cref="String"/>.</value>
        public string LastName {
            get { return GetProperty( LastNameProperty ); }
            set { SetProperty<string>( LastNameProperty, value ); }
        }

        /// <summary>Value to search
        /// for with in the contacts
        /// postal code.</summary>
        /// <value>A <see cref="String"/>.</value>
        public string PostalCode {
            get { return GetProperty( PostalCodeProperty ); }
            set { SetProperty<string>( PostalCodeProperty, value ); }
        }

        /// <summary>Value to search
        /// for with in the contacts
        /// country.</summary>
        /// <value>A <see cref="String"/>.</value>
        public string Country {
            get { return GetProperty( CountryProperty ); }
            set { SetProperty<string>( CountryProperty, value ); }
        }

        /// <summary>Value to search
        /// for with in the contacts
        /// title.</summary>
        /// <value>A <see cref="String"/>.</value>
        public string Title {
            get { return GetProperty( TitleProperty ); }
            set { SetProperty<string>( TitleProperty, value ); }
        }

        /// <summary>Value to search
        /// for with in the contacts
        /// salutation.</summary>
        /// <value>A <see cref="String"/>.</value>
        public string Salutation {
            get { return GetProperty( SalutationProperty ); }
            set { SetProperty<string>( SalutationProperty, value ); }
        }

        /// <summary>Value to search
        /// for with in the contacts
        /// city.</summary>
        /// <value>A <see cref="String"/>.</value>
        public string City {
            get { return GetProperty( CityProperty ); }
            set { SetProperty<string>( CityProperty, value ); }
        }

        /// <summary>Value to search
        /// for with in the contacts
        /// address line 1.</summary>
        /// <value>A <see cref="String"/>.</value>
        public string AddressLine1 {
            get { return GetProperty( AddressLine1Property ); }
            set { SetProperty<string>( AddressLine1Property, value ); }
        }

        /// <summary>Value to search
        /// for with in the contacts
        /// address line 2.</summary>
        /// <value>A <see cref="String"/>.</value>
        public string AddressLine2 {
            get { return GetProperty( AddressLine2Property ); }
            set { SetProperty<string>( AddressLine2Property, value ); }
        }

        /// <summary>Value to search
        /// for with in the contacts
        /// address line 3.</summary>
        /// <value>A <see cref="String"/>.</value>
        public string AddressLine3 {
            get { return GetProperty( AddressLine3Property ); }
            set { SetProperty<string>( AddressLine3Property, value ); }
        }

        /// <summary>Value to search
        /// for with in the contacts
        /// state.</summary>
        /// <value>A <see cref="String"/>.</value>
        public string State {
            get { return GetProperty( StateProperty ); }
            set { SetProperty<string>( StateProperty, value ); }
        }

        /// <summary>Value to search
        /// for with in the contacts
        /// email.</summary>
        /// <value>A <see cref="String"/>.</value>
        public string Email {
            get { return GetProperty( EmailProperty ); }
            set { SetProperty<string>( EmailProperty, value ); }
        }

        #endregion

        #region Factory Methods

        /// <summary>Creates a new instance.</summary>
        /// <returns>A <see cref="ContactListSearchCriteria"/>.</returns>
        public static ContactListSearchCriteria NewCriteria() {
            return DataPortal.Create<ContactListSearchCriteria>();
        }

        #endregion

        #region Business Base overrides

        /// <summary>Sets business rules for the type.</summary>
        protected override void AddBusinessRules() {
            ValidationRules.AddRule<ContactListSearchCriteria>(
                EnsureSomethingSet,
                DummyProperty
            );

            ValidationRules.AddDependentProperty( FirstNameProperty, DummyProperty );
            ValidationRules.AddDependentProperty( LastNameProperty, DummyProperty );
            ValidationRules.AddDependentProperty( CompanyProperty, DummyProperty );
            ValidationRules.AddDependentProperty( PostalCodeProperty, DummyProperty );
            ValidationRules.AddDependentProperty( TitleProperty, DummyProperty );
            ValidationRules.AddDependentProperty( CountryProperty, DummyProperty );
            ValidationRules.AddDependentProperty( SalutationProperty, DummyProperty );
            ValidationRules.AddDependentProperty( CityProperty, DummyProperty );
            ValidationRules.AddDependentProperty( AddressLine1Property, DummyProperty );
            ValidationRules.AddDependentProperty( AddressLine2Property, DummyProperty );
            ValidationRules.AddDependentProperty( AddressLine3Property, DummyProperty );
            ValidationRules.AddDependentProperty( StateProperty, DummyProperty );
            ValidationRules.AddDependentProperty( EmailProperty, DummyProperty );
        }

        #endregion

        #region Business rules

        /// <summary>Ensures that at least one property is not
        /// empty.</summary>
        /// <param name="target">The instance to check.</param>
        /// <param name="e">Rule args.</param>
        /// <returns><c>true</c> if the rule is met,
        /// <c>false</c> otherwise.</returns>
        private static bool EnsureSomethingSet(
            ContactListSearchCriteria target,
            RuleArgs e
        ) {
            bool result;

            result = !(
                string.IsNullOrEmpty( target.ReadProperty( AddressLine1Property ) ) &&
                string.IsNullOrEmpty( target.ReadProperty( AddressLine2Property ) ) &&
                string.IsNullOrEmpty( target.ReadProperty( AddressLine3Property ) ) &&
                string.IsNullOrEmpty( target.ReadProperty( CityProperty ) ) &&
                string.IsNullOrEmpty( target.ReadProperty( CountryProperty ) ) &&
                string.IsNullOrEmpty( target.ReadProperty( PostalCodeProperty ) ) &&
                string.IsNullOrEmpty( target.ReadProperty( StateProperty ) ) &&
                string.IsNullOrEmpty( target.ReadProperty( TitleProperty ) ) &&
                string.IsNullOrEmpty( target.ReadProperty( CompanyProperty ) ) &&
                string.IsNullOrEmpty( target.ReadProperty( FirstNameProperty ) ) &&
                string.IsNullOrEmpty( target.ReadProperty( LastNameProperty ) ) &&
                string.IsNullOrEmpty( target.ReadProperty( SalutationProperty ) ) &&
                string.IsNullOrEmpty( target.ReadProperty( EmailProperty ) )
            );

            if ( !result ) {
                e.Description = Properties.Resources.ContactListSearchCriteriaNoCriteria;
            }

            return result;
        }

        #endregion

        #region Data Access

        /// <summary>Initalizes the instance.</summary>
        [RunLocal]
        protected override void DataPortal_Create() {
            ValidationRules.CheckRules();
        }

        #endregion

        #region Constructors

        /// <summary>Prevent direct creation.</summary>
        private ContactListSearchCriteria() { }
       
        #endregion

        #region Object overrides

        /// <summary>Returns the text of the
        /// search criteria.</summary>
        /// <returns>A <see cref="String"/>
        /// listing all the criteria.</returns>
        public override string ToString() {
            string result;

            result = "";

            if ( !string.IsNullOrEmpty( FirstName ) ) {
                if ( !string.IsNullOrEmpty( result ) ) {
                    result += "; ";
                }
                result += string.Format( "First name = {0}", FirstName );
            }

            if ( !string.IsNullOrEmpty( LastName ) ) {
                if ( !string.IsNullOrEmpty( result ) ) {
                    result += "; ";
                }
                result += string.Format( "Last name = {0}", LastName );
            }

            if ( !string.IsNullOrEmpty( Title ) ) {
                if ( !string.IsNullOrEmpty( result ) ) {
                    result += "; ";
                }
                result += string.Format( "Title = {0}", Title );
            }

            if ( !string.IsNullOrEmpty( Salutation ) ) {
                if ( !string.IsNullOrEmpty( result ) ) {
                    result += "; ";
                }
                result += string.Format( "Salutation = {0}", Salutation );
            }

            if ( !string.IsNullOrEmpty( Email ) ) {
                if ( !string.IsNullOrEmpty( result ) ) {
                    result += "; ";
                }
                result += string.Format( "Email = {0}", Email );
            }

            if ( !string.IsNullOrEmpty( AddressLine1 ) ) {
                if ( !string.IsNullOrEmpty( result ) ) {
                    result += "; ";
                }
                result += string.Format( "Line1 = {0}", AddressLine1 );
            }

            if ( !string.IsNullOrEmpty( AddressLine2 ) ) {
                if ( !string.IsNullOrEmpty( result ) ) {
                    result += "; ";
                }
                result += string.Format( "Line2 = {0}", AddressLine2 );
            }

            if ( !string.IsNullOrEmpty( AddressLine3 ) ) {
                if ( !string.IsNullOrEmpty( result ) ) {
                    result += "; ";
                }
                result += string.Format( "Line3 = {0}", AddressLine3 );
            }

            if ( !string.IsNullOrEmpty( City ) ) {
                if ( !string.IsNullOrEmpty( result ) ) {
                    result += "; ";
                }
                result += string.Format( "City = {0}", City );
            }

            if ( !string.IsNullOrEmpty( State ) ) {
                if ( !string.IsNullOrEmpty( result ) ) {
                    result += "; ";
                }
                result += string.Format( "State = {0}", State );
            }

            if ( !string.IsNullOrEmpty( PostalCode ) ) {
                if ( !string.IsNullOrEmpty( result ) ) {
                    result += "; ";
                }
                result += string.Format( "Postal Code = {0}", PostalCode );
            }

            if ( !string.IsNullOrEmpty( Country ) ) {
                if ( !string.IsNullOrEmpty( result ) ) {
                    result += "; ";
                }
                result += string.Format( "Country = {0}", Country );
            }

            if ( !string.IsNullOrEmpty( Company ) ) {
                if ( !string.IsNullOrEmpty( result ) ) {
                    result += "; ";
                }
                result += string.Format( "Company = {0}", Company );
            }

            if ( string.IsNullOrEmpty( result ) ) {
                result = "No criteria";
            }

            return result;
        }

        #endregion

        #region ICriteria Members

        Type ICriteria.ObjectType {
            get { return typeof( ContactList ); }
        }

        #endregion
    }

ajj3085 replied on Tuesday, March 24, 2009

Well, I do disagree.  Many users expect instant reactions.  Especially since one of the affected forms has a single textbox and find and cancel buttons (it's a search form).  They have to enter at least one letter, and tabbing to the cancel button (because Find is disabled) was very confusing.  The other affected forms were for entering search criteria as well, so we're talking about 10 textboxes at most.  It really shouldn't be that big of a performance issue to re-read 9 values.

The problem I had in tracking this done is that the only changes were to the BOs, using PropertyInfo instead of the old-school method, and using Infragistics dropdowns in place of the standard ones.   Of course as I investigated I ruled these out... going back to the old-school csla objects and standard dropdowns, and still having problems.  As I said, nothing else changed, certainly not the attaching of events, at least nothing I'm attaching.

The only thing that seemed odd was how many times the dropdowns where looking up values in some NVLB subclasses, but I think that was just the Infragistics controls doing that.

ajj3085 replied on Tuesday, March 24, 2009

I'm  using Winforms.

It's odd; I have a bunch of other screens that are still using the OnPropertyChanged to determine when to update the bound BO, and them seem to be fine. I'm really at a loss.

Andy

Copyright (c) Marimer LLC