как уведомить модификации дочернего объекта в родительский объект, используя ObjectWithChangeTracker - PullRequest
2 голосов
/ 04 января 2012

Созданные объекты с использованием Ado.net Self Tracking Entity Generator для поддержки отслеживания состояния.Объекты Пользователь, Тренер.Тренер является дочерним объектом в User.Когда информация изменена в Trainer, ее состояние изменяется как измененное.но пользовательский объект все еще остается неизменным.Мое требование заключается в том, что когда бы ни изменялся дочерний объект, он должен быть связан с родительским объектом.

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 равен

//------------------------------------------------------------------------------
// <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
    }
}

1 Ответ

0 голосов
/ 18 января 2012

Ты не хочешь этого делать. Это означает, что когда у вас есть более сложные графы объектов и какой-то экземпляр объекта изменился, это будет распространено на всех родителей, лежащих выше. Это не такая уж большая проблема. Но, применяя изменения к ObjectContext для сохраняющихся сущностей, FrameWork также обновит все эти родительские объекты (когда ваша цель - распространить изменения на ChangeTracker родительских (ых) -> ChangeTracker является используемым объектом FrameWork для обнаружения изменений и отправки обновлений, вставки или удаления запросов в базу данных.

Платформа Entity Framework сама по себе не включает этот вид «каскадной» информации для сущностей с самостоятельным отслеживанием.

Нам нужна была похожая функциональность. Легко обнаружить объект «верхнего уровня», если под ним что-то изменилось. Мы сделали рекурсивный метод для проверки всех базовых объектов и State их ChangeTrackers. Осторожно: вам может понадобиться не только взглянуть на entity.ChangeTracker.State, но и на entity.ChangeTracker.AddedToCollectionProperties и entity.ChangeTracker.RemovedFromCollectionProperties, чтобы увидеть, были ли какие-либо объекты добавлены или удалены из свойств навигации (которые в основном также являются изменениями).

Другим вариантом может быть включение простого Enum (Очистить, Изменено, Добавлено, Удалено и т. Д.) В каждую сущность, а при внесении изменений обновите переменную Enum для сущности и вышестоящих «родителей». Таким образом, вы не будете вмешиваться в ChangeTracker, с которым я бы не стал слишком часто связываться.

Надеюсь, это поможет.

...