2

Created Objects using Ado.net Self Tracking Entity Generator for maintain the state tracker. The Objects are User, Trainer. Trainer is the child object in User. When a information modified in Trainer, it state is changed as modified. but the user object is still unchanged. My requirement is when ever a child object modified it has to intimate to the parent object.

User.cs

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated from a template.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.Serialization;

namespace ControlLibrary
{
    [DataContract(IsReference = true)]
    [KnownType(typeof(Trainer))]
    public partial class User: IObjectWithChangeTracker, INotifyPropertyChanged
    {
        #region Primitive Properties

        [DataMember]
        public int UserId
        {
            get { return _userId; }
            set
            {
                if (_userId != value)
                {
                    if (ChangeTracker.ChangeTrackingEnabled && ChangeTracker.State != ObjectState.Added)
                    {
                        throw new InvalidOperationException("The property 'UserId' is part of the object's key and cannot be changed. Changes to key properties can only be made when the object is not being tracked or is in the Added state.");
                    }
                    _userId = value;
                    OnPropertyChanged("UserId");
                }
            }
        }
        private int _userId;

        [DataMember]
        public string UserName
        {
            get { return _userName; }
            set
            {
                if (_userName != value)
                {
                    _userName = value;
                    OnPropertyChanged("UserName");
                }
            }
        }
        private string _userName;

        #endregion
        #region Navigation Properties

        [DataMember]
        public Trainer Trainer
        {
            get { return _trainer; }
            set
            {
                if (!ReferenceEquals(_trainer, value))
                {
                    var previousValue = _trainer;
                    _trainer = value;
                    FixupTrainer(previousValue);
                    OnNavigationPropertyChanged("Trainer");
                }
            }
        }
        private Trainer _trainer;

        #endregion
        #region ChangeTracking

        protected virtual void OnPropertyChanged(String propertyName)
        {
            if (ChangeTracker.State != ObjectState.Added && ChangeTracker.State != ObjectState.Deleted)
            {
                ChangeTracker.State = ObjectState.Modified;
            }
            if (_propertyChanged != null)
            {
                _propertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        protected virtual void OnNavigationPropertyChanged(String propertyName)
        {
            if (_propertyChanged != null)
            {
                _propertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged{ add { _propertyChanged += value; } remove { _propertyChanged -= value; } }
        private event PropertyChangedEventHandler _propertyChanged;
        private ObjectChangeTracker _changeTracker;

        [DataMember]
        public ObjectChangeTracker ChangeTracker
        {
            get
            {
                if (_changeTracker == null)
                {
                    _changeTracker = new ObjectChangeTracker();
                    _changeTracker.ObjectStateChanging += HandleObjectStateChanging;
                }
                return _changeTracker;
            }
            set
            {
                if(_changeTracker != null)
                {
                    _changeTracker.ObjectStateChanging -= HandleObjectStateChanging;
                }
                _changeTracker = value;
                if(_changeTracker != null)
                {
                    _changeTracker.ObjectStateChanging += HandleObjectStateChanging;
                }
            }
        }

        private void HandleObjectStateChanging(object sender, ObjectStateChangingEventArgs e)
        {
            if (e.NewState == ObjectState.Deleted)
            {
                ClearNavigationProperties();
            }
        }

        protected bool IsDeserializing { get; private set; }

        [OnDeserializing]
        public void OnDeserializingMethod(StreamingContext context)
        {
            IsDeserializing = true;
        }

        [OnDeserialized]
        public void OnDeserializedMethod(StreamingContext context)
        {
            IsDeserializing = false;
            ChangeTracker.ChangeTrackingEnabled = true;
        }

        protected virtual void ClearNavigationProperties()
        {
            Trainer = null;
        }

        #endregion
        #region Association Fixup

        private void FixupTrainer(Trainer previousValue)
        {
            // This is the principal end in an association that performs cascade deletes.
            // Update the event listener to refer to the new dependent.
            if (previousValue != null)
            {
                ChangeTracker.ObjectStateChanging -= previousValue.HandleCascadeDelete;
            }

            if (Trainer != null)
            {
                ChangeTracker.ObjectStateChanging += Trainer.HandleCascadeDelete;
            }

            if (IsDeserializing)
            {
                return;
            }

            if (previousValue != null && ReferenceEquals(previousValue.User, this))
            {
                previousValue.User = null;
            }

            if (Trainer != null)
            {
                Trainer.User = this;
            }

            if (ChangeTracker.ChangeTrackingEnabled)
            {
                if (ChangeTracker.OriginalValues.ContainsKey("Trainer")
                    && (ChangeTracker.OriginalValues["Trainer"] == Trainer))
                {
                    ChangeTracker.OriginalValues.Remove("Trainer");
                }
                else
                {
                    ChangeTracker.RecordOriginalValue("Trainer", previousValue);
                    // This is the principal end of an identifying association, so the dependent must be deleted when the relationship is removed.
                    // If the current state of the dependent is Added, the relationship can be changed without causing the dependent to be deleted.
                    if (previousValue != null && previousValue.ChangeTracker.State != ObjectState.Added)
                    {
                        previousValue.MarkAsDeleted();
                    }
                }
                if (Trainer != null && !Trainer.ChangeTracker.ChangeTrackingEnabled)
                {
                    Trainer.StartTracking();
                }
            }
        }

        #endregion
    }
}

Trainer.cs file is

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated from a template.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.Serialization;

namespace ControlLibrary
{
    [DataContract(IsReference = true)]
    [KnownType(typeof(User))]
    public partial class Trainer: IObjectWithChangeTracker, INotifyPropertyChanged
    {
        #region Primitive Properties

        [DataMember]
        public int TrainerId
        {
            get { return _trainerId; }
            set
            {
                if (_trainerId != value)
                {
                    if (ChangeTracker.ChangeTrackingEnabled && ChangeTracker.State != ObjectState.Added)
                    {
                        throw new InvalidOperationException("The property 'TrainerId' is part of the object's key and cannot be changed. Changes to key properties can only be made when the object is not being tracked or is in the Added state.");
                    }
                    if (!IsDeserializing)
                    {
                        if (User != null && User.UserId != value)
                        {
                            User = null;
                        }
                    }
                    _trainerId = value;
                    OnPropertyChanged("TrainerId");
                }
            }
        }
        private int _trainerId;

        [DataMember]
        public int UserId
        {
            get { return _userId; }
            set
            {
                if (_userId != value)
                {
                    _userId = value;
                    OnPropertyChanged("UserId");
                }
            }
        }
        private int _userId;

        [DataMember]
        public Nullable<bool> IsFloorTrainer
        {
            get { return _isFloorTrainer; }
            set
            {
                if (_isFloorTrainer != value)
                {
                    _isFloorTrainer = value;
                    OnPropertyChanged("IsFloorTrainer");
                }
            }
        }
        private Nullable<bool> _isFloorTrainer;

        #endregion
        #region Navigation Properties

        [DataMember]
        public User User
        {
            get { return _user; }
            set
            {
                if (!ReferenceEquals(_user, value))
                {
                    if (ChangeTracker.ChangeTrackingEnabled && ChangeTracker.State != ObjectState.Added && value != null)
                    {
                        // This the dependent end of an identifying relationship, so the principal end cannot be changed if it is already set,
                        // otherwise it can only be set to an entity with a primary key that is the same value as the dependent's foreign key.
                        if (TrainerId != value.UserId)
                        {
                            throw new InvalidOperationException("The principal end of an identifying relationship can only be changed when the dependent end is in the Added state.");
                        }
                    }
                    var previousValue = _user;
                    _user = value;
                    FixupUser(previousValue);
                    OnNavigationPropertyChanged("User");
                }
            }
        }
        private User _user;

        #endregion
        #region ChangeTracking

        protected virtual void OnPropertyChanged(String propertyName)
        {
            if (ChangeTracker.State != ObjectState.Added && ChangeTracker.State != ObjectState.Deleted)
            {
                ChangeTracker.State = ObjectState.Modified;
            }
            if (_propertyChanged != null)
            {
                _propertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        protected virtual void OnNavigationPropertyChanged(String propertyName)
        {
            if (_propertyChanged != null)
            {
                _propertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged{ add { _propertyChanged += value; } remove { _propertyChanged -= value; } }
        private event PropertyChangedEventHandler _propertyChanged;
        private ObjectChangeTracker _changeTracker;

        [DataMember]
        public ObjectChangeTracker ChangeTracker
        {
            get
            {
                if (_changeTracker == null)
                {
                    _changeTracker = new ObjectChangeTracker();
                    _changeTracker.ObjectStateChanging += HandleObjectStateChanging;
                }
                return _changeTracker;
            }
            set
            {
                if(_changeTracker != null)
                {
                    _changeTracker.ObjectStateChanging -= HandleObjectStateChanging;
                }
                _changeTracker = value;
                if(_changeTracker != null)
                {
                    _changeTracker.ObjectStateChanging += HandleObjectStateChanging;
                }
            }
        }

        private void HandleObjectStateChanging(object sender, ObjectStateChangingEventArgs e)
        {
            if (e.NewState == ObjectState.Deleted)
            {
                ClearNavigationProperties();
            }
        }

        protected bool IsDeserializing { get; private set; }

        [OnDeserializing]
        public void OnDeserializingMethod(StreamingContext context)
        {
            IsDeserializing = true;
        }

        [OnDeserialized]
        public void OnDeserializedMethod(StreamingContext context)
        {
            IsDeserializing = false;
            ChangeTracker.ChangeTrackingEnabled = true;
        }

        // This entity type is the dependent end in at least one association that performs cascade deletes.
        // This event handler will process notifications that occur when the principal end is deleted.
        internal void HandleCascadeDelete(object sender, ObjectStateChangingEventArgs e)
        {
            if (e.NewState == ObjectState.Deleted)
            {
                this.MarkAsDeleted();
            }
        }

        protected virtual void ClearNavigationProperties()
        {
            User = null;
        }

        #endregion
        #region Association Fixup

        private void FixupUser(User previousValue)
        {
            if (IsDeserializing)
            {
                return;
            }

            if (previousValue != null && ReferenceEquals(previousValue.Trainer, this))
            {
                previousValue.Trainer = null;
            }

            if (User != null)
            {
                User.Trainer = this;
                TrainerId = User.UserId;
            }

            if (ChangeTracker.ChangeTrackingEnabled)
            {
                if (ChangeTracker.OriginalValues.ContainsKey("User")
                    && (ChangeTracker.OriginalValues["User"] == User))
                {
                    ChangeTracker.OriginalValues.Remove("User");
                }
                else
                {
                    ChangeTracker.RecordOriginalValue("User", previousValue);
                }
                if (User != null && !User.ChangeTracker.ChangeTrackingEnabled)
                {
                    User.StartTracking();
                }
            }
        }

        #endregion
    }
}
VIJAY
  • 849
  • 11
  • 22
  • do you have code examples.. also look at adding Events or creating a Delegate or AnonymousDelegate to handle the Parent/ Child events – MethodMan Jan 04 '12 at 18:19
  • The Code generated by Ado.net Self Tracking entity generator is avaliable in this link [url=http://www.fileserve.com/file/nUwkHra/ControlLibrary.rar][b]File name: ControlLibrary.rar File size: 15.97 KB[/b][/url] – VIJAY Jan 04 '12 at 18:30
  • individuals will not download your .rar file probably due to their internal firewall and security so paste what code is necessary and applies to your question.. thanks – MethodMan Jan 04 '12 at 18:34
  • I have placed the user.cs and trainer.cs – VIJAY Jan 05 '12 at 01:19

1 Answers1

1

You don't want to do this. It means that when you have more complex object graphs and some instance of an object has changed this will be propagated up to all parents lying above. That's not so much of a problem still. But, when applying changes to an ObjectContext for persisting entities the FrameWork will also update all those parents (when your target is to propagate the changes to the ChangeTracker of parent(s) -> The ChangeTracker is the object used by the FrameWork to detect changes and sending update, insert or delete queries to the database).

Entity Framework does not include this kind of "cascading" information by itself for Self Tracking Entities.

We needed similiar functionality. To detect easily for a "top-level" object if anything had changed underneath. We made a recursive method to check out all underlying entities and the State of their ChangeTrackers. Beware: you might not only need to look at entity.ChangeTracker.State but also at entity.ChangeTracker.AddedToCollectionProperties and entity.ChangeTracker.RemovedFromCollectionProperties to see if any entities were added or removed from navigation properties (which are basically also changes).

Another option would be to to include a simple Enum (Clean, Modified, Added, Deleted or so...) on every entity and when changes are made update the Enum variable on the entity and the "parents" above. This way you don't interfere with the ChangeTracker which I would not mess with too much.

Hope this helps.

Youp Bernoulli
  • 5,303
  • 5
  • 39
  • 59