Capture changes on child entities

Posts   
 
    
juangelos
User
Posts: 15
Joined: 30-Mar-2009
# Posted on: 31-Mar-2009 23:25:23   

Scenario:

Order - [1:n] - Detail

I need to capture (preferably through an event in the orderEntity.Detail EntityCollection) changes to the Detail instances fields.

eg. I would like to add a method [OnDetailChanged] in an OrderEntity partial class which captures any changes in the orderEntity.Detail instances (does not suffice with the Dirty flag, I need to be notified of any changes).

In the actual scenario, I need to do this in order to recalculate the Order.Subtotal, Order.Taxes, Order.Total fields by traversing the Details collection anytime the fields "ProductId", "Price" or "Quantity" are changed in a Detail instance belonging to the Order.

So the ideal would be to plug into the "OnPropertyChanged" for each Detail instance, (maybe on EntityCollection.Add()?), check which property has changed, and fire my own OnDetailsChanged event based on that.

Which methods should I override / plug in to? OR,_ is there a better way to "architecture" this?_

LLBLGen 2.6 Final October 2008, SelfServicing, General2008 preset.

juangelos
User
Posts: 15
Joined: 30-Mar-2009
# Posted on: 01-Apr-2009 00:13:03   

How does this look?


using System;
using System.ComponentModel;

using Pulsar2.Facturación.Data.EntityClasses;

using SD.LLBLGen.Pro.ORMSupportClasses;


namespace Pulsar2.Facturación.Data.CollectionClasses
{
    public partial class FacturaIitemCollection
    {
        protected override bool OnEntityAdding(FacturaIitemEntity entityToAdd)
        {
            entityToAdd.PropertyChanged += this.item_PropertyChanged;
            return base.OnEntityAdding(entityToAdd);
        }


        protected override bool OnEntityRemoving(FacturaIitemEntity entityToRemove)
        {
            entityToRemove.PropertyChanged -= this.item_PropertyChanged;
            return base.OnEntityRemoving(entityToRemove);
        }


        private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            this.OnItemChanged(new ItemChangedEventArgs(sender as IEntity2, e.PropertyName));
        }


        public event EventHandler<ItemChangedEventArgs> ItemChanged;


        public void OnItemChanged(ItemChangedEventArgs e)
        {
            if (this.ItemChanged != null)
            {
                this.ItemChanged(this, e);
            }
        }


        #region Nested type: ItemChangedEventArgs
        public class ItemChangedEventArgs : EventArgs
        {
            public ItemChangedEventArgs(IEntity2 item, string propertyName)
            {
                this.Item = item;
                this.PropertyName = propertyName;
            }


            public IEntity2 Item { get; private set; }
            public string PropertyName { get; set; }
        }
        #endregion
    }
}

juangelos
User
Posts: 15
Joined: 30-Mar-2009
# Posted on: 01-Apr-2009 00:14:14   

OnEntityRemoving or OnEntityRemoved?

I chose Removing because I don't want the event triggering because of changes to the entity while it's being removed from the collection.

juangelos
User
Posts: 15
Joined: 30-Mar-2009
# Posted on: 04-Apr-2009 12:18:14   

bump?

Walaa avatar
Walaa
Support Team
Posts: 14993
Joined: 21-Aug-2005
# Posted on: 06-Apr-2009 11:42:20   

This was discussed here: http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=12849

You may also listen to the Order.Detail entitycollections ListChanged event. And if you are editing this in a grid you may try the grid's events.

rdhatch
User
Posts: 198
Joined: 03-Nov-2007
# Posted on: 06-Apr-2009 14:49:46   

Hi Juan,

I've coded all of this successfully. I have an observer class that monitors a Root Entity & all its children recursively. So, you can do calculated fields on children n-levels deep.

Here is the basic principle (minus all the complex Recursion):

  • Subscribe to ChildCollection.EntityAdded & ChildCollection.EntityRemoved Events - Listens for Collection changes
  • Subscribe to either all Child Entity.EntityChanged Events or ChildCollection.ListChanged - Listens for changes to Child values
  • Upon a Change - Call OnPropertyChanged("MyCalculatedField") to update the Calculated field in your Root Entity. This will use INotifyPropertyChanged to update all databound controls.Hope this helps!

Ryan

PS. I've gotten this to work so smoothly... All I have to do in each Entity's partial class is implement my ICalculatedFields interface. My CommonEntityBase automatically knows which properties are calculated fields and automatically updates them using .OnPropertyChanged() whenever the entity is changed. CommonEntityBase knows how to identify a calculated property from a real DB property by analyzing the HelperClasses.EntityFields code below. Notice how these are shared fields, not properties. Reflection can tell the difference.

So, in the same file as the partial Entity class, I also have a partial class for that Entity's Fields (shown below). Notice the Constructor - Calling a method that automatically creates new EntityFields objects and assigns them to these shared fields using Reflection. This way, I never use strings to identify my calculated fields - I always use strongly-typed EntityField objects.

For example: I can databind a readonly Label to - ProductionOrderFields.PricePerPound

And I can even allow my Calculated fields to be writable, which in turn updates a real Entity value, which in turn fires .OnPropertyChanged() for any other dependent calculated fields.

Namespace NovocDataObjects.HelperClasses
    Partial Public Class ProductionOrderFields

        '---------------------------------------------------------------------------
        'Calculated Fields
        '---------------------------------------------------------------------------
        Public Shared LotNumber As EntityField
        Public Shared PricePerPound As EntityField
        Public Shared PricePerGallon As EntityField
        Public Shared TotalPrice As EntityField
        Public Shared CanProduce As EntityField
        Public Shared OrderQuantityLbsIncludingWaste As EntityField
        Public Shared ProductionOrderStatus As EntityField
        Public Shared QualityResult As EntityField
        Public Shared OrderQuantityGals As EntityField

        '---------------------------------------------------------------------------
        'Fields on Related Entities
        '---------------------------------------------------------------------------
        Public Shared FinishedProductName As EntityField
        Public Shared FinishedProductDescription As EntityField
        Public Shared CustomerName As EntityField
        Public Shared Ponumber As EntityField
        Public Shared SalesOrderNumber As EntityField

        Shared Sub New()
            KonectCommonVWG.LLBLGenCommon.HelperClasses.AutomaticallyCreateEntityFields()
        End Sub

    End Class

End Namespace
Walaa avatar
Walaa
Support Team
Posts: 14993
Joined: 21-Aug-2005
# Posted on: 06-Apr-2009 15:25:57   

Ryan,

Thank you very much for the contribution.