"infinite loop" with DA and PropertyChangedEvent

I have an app that has been in production for awhile and yesterday I started getting emails about my app crashing. App built with latest Elements and DA. I did some debugging and i discovered this morning that now i seem to be caught in an loop in a PropertyChangedEvent. That event gets triggered from one field change, and in that event i update a second field, which then causes the event to get called again, and again, and again…

Is there a “proper” way to use this event in this manner without having your code go into a loop of death like this? As i said…this has worked up until now without any changes in this part of the code, so on a different level i’m a bit confused as to why this is an issue now.

Thanks for any help or advice.

Hello

Could you create a testcase for this issue? It is hard to say what is going wrong without actual code. You can drop the code to support@ so it will be kept private.

Thanks in advance

I can try and put something together but are there preferred practices to update field2 in this event after changing field1? Should this be ok?

If you have a situation that looks like

Set property A -> Raise event for property A ->
Set property B in the event handler -> Raise event for property B
-> Set property A in the event handler -> ...

then you have a circular dependency between properties and this code will (and should) crash.

Instead of relying on PropertyChaneEvent you could update property underlying fields in property setter and then raise PropertyChanged event for all changed properties.

How do i override the individual field setters? Do i need to move those declarations to the public section of the generated DA classes? I can’t edit the DA tabledefinitions classes since those are autogenerated by DA. What is the best approach to intercept the setters for individual fields?

So a class in question is a DA-generated Table Definition class?

As I understand you have 2 fields in a table that depend on each other and you need to keep them in sync.
There are several approaches for this (like defining separate calculated property or a method, or even modifying the generated source code).

Still you need to check a thing first

Please check your calculation logic. If there are 2 properties that depend on each other via some calculations then there should be no infinite event raisin loop. Take a look at this generated property setter:

method Employees.set__HomePhone(___value___: System.String);
begin
  if System.Collections.Generic.Comparer<System.String>.Default.Compare(self.f____HomePhone, ___value___) ≠ 0 then begin
    self.OnPropertyChanging('HomePhone');
    self.f____HomePhone := ___value___;
    self.OnPropertyChanged('HomePhone');
  end;
end;

PropertyChanged event in not raised at all if you try to set a property to a value matching its current value. So there should be no dead events loop.

For WPF data binding purposes it might be easier to define a separate composite property. F.e. let’s assume that we have a Table Defintion class defined as

  [RemObjects.DataAbstract.Linq.TableName('Employees')]
  Employees = public partial class(System.ICloneable, System.ComponentModel.INotifyPropertyChanged, System.ComponentModel.INotifyPropertyChanging)
...
    [RemObjects.DataAbstract.Linq.FieldName('LastName')]
    [RemObjects.DataAbstract.Linq.DataType(RemObjects.DataAbstract.Schema.DataType.WideString)]
    [RemObjects.DataAbstract.Linq.LogChanges]
    property LastName: System.String read get__LastName write set__LastName;

    [RemObjects.DataAbstract.Linq.FieldName('FirstName')]
    [RemObjects.DataAbstract.Linq.DataType(RemObjects.DataAbstract.Schema.DataType.WideString)]
    [RemObjects.DataAbstract.Linq.LogChanges]
    property FirstName: System.String read get__FirstName write set__FirstName;
...

Note that this class is defined as partial.
To add a property you need to add a separate code file (that won’t be changed if you regenerate the table definition classes) and put there this code:

type
  Employees = public partial class
  private
    method get_FullName(): String;
    begin
      exit self.FirstName + " " + self.LastName;
    end;

    method set_FullName(value: String);
    begin
      // Somehow split value to first name and last name parts
      // Skipping this logic here
      var fn := '....';
      var ln := '....';

      self.f____FirstName := fn;
      self.f____LastName := ln;

      self.OnPropertyChanged('FullName');
      self.OnPropertyChanged('FirstName');
      self.OnPropertyChanged('LastName');
    end;

  public
    [RemObjects.DataAbstract.Linq.IgnoreProperty]
    property FullName: String read get_FullName write set_FullName;
  end;

This composite property now can be used for data binding etc. Note that changing this property also raises property notification events for related properties as well.