c# - 如何使用 ObjectWithChangeTracker 通知子对象对父对象的修改

标签 c# self-tracking-entities

使用 Ado.net 自跟踪实体生成器创建对象以维护状态跟踪器。对象是用户、培训师。 Trainer 是 User 中的子对象。当在 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(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
    }
}

最佳答案

你不想这样做。这意味着当您有更复杂的对象图并且对象的某些实例已更改时,这将传播到位于上面的所有父对象。这仍然不是什么大问题。但是,当为持久化实体对 ObjectContext 应用更改时,FrameWork 也会更新所有这些父项(当您的目标是将更改传播到父项的 ChangeTracker 时) -> ChangeTracker 是 FrameWork 用于检测更改并向数据库发送更新、插入或删除查询的对象。

Entity Framework 自身不包含这种“级联”信息以用于自跟踪实体。

我们需要类似的功能。如果下面有任何变化,可以轻松检测“顶级”对象。我们采用递归方法检查所有底层实体及其 ChangeTrackersState。当心:您可能不仅需要查看 entity.ChangeTracker.State,还需要查看 entity.ChangeTracker.AddedToCollectionPropertiesentity.ChangeTracker.RemovedFromCollectionProperties查看是否在导航属性中添加或删除了任何实体(这基本上也是更改)。

另一种选择是在每个实体上包含一个简单的枚举(Clean、Modified、Added、Deleted 等等...),并在进行更改时更新实体上的 Enum 变量和上面的“父级”。这样您就不会干扰 ChangeTracker,我不会过多地干扰它。

希望这对您有所帮助。

关于c# - 如何使用 ObjectWithChangeTracker 通知子对象对父对象的修改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8731791/

相关文章:

c# - 难以结合使用基本构造函数和非静态字段

c# - self 跟踪实体 SaveChanges() 在多对多关系中添加实体时出现异常

c# - 在已编译的 LINQ 查询中使用 lambda Include 方法

c# - 如何在C#中动态创建连接字符串

c# - 是否可以让匿名类继承另一个类?

c# - Visual Studio 2013 中的 FxCop 和代码分析

c# - 在不连接到数据库的情况下创建 DbContext

entity-framework - 自跟踪实体 - 添加到集合时尝试插入未更改的实体

c# - Entity Framework 拆分表删除