using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; namespace Umbraco.Core.Models.EntityBase { /// /// A base class for use to implement IRememberBeingDirty/ICanBeDirty /// public abstract class TracksChangesEntityBase : IRememberBeingDirty { /// /// Tracks the properties that have changed /// private readonly IDictionary _propertyChangedInfo = new Dictionary(); /// /// Tracks the properties that we're changed before the last commit (or last call to ResetDirtyProperties) /// private IDictionary _lastPropertyChangedInfo = null; /// /// Property changed event /// public event PropertyChangedEventHandler PropertyChanged; /// /// Method to call on a property setter. /// /// The property info. protected virtual void OnPropertyChanged(PropertyInfo propertyInfo) { _propertyChangedInfo[propertyInfo.Name] = true; if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyInfo.Name)); } } /// /// Indicates whether a specific property on the current entity is dirty. /// /// Name of the property to check /// True if Property is dirty, otherwise False public virtual bool IsPropertyDirty(string propertyName) { return _propertyChangedInfo.Any(x => x.Key == propertyName); } /// /// Indicates whether the current entity is dirty. /// /// True if entity is dirty, otherwise False public virtual bool IsDirty() { return _propertyChangedInfo.Any(); } /// /// Indicates that the entity had been changed and the changes were committed /// /// public bool WasDirty() { return _lastPropertyChangedInfo != null && _lastPropertyChangedInfo.Any(); } /// /// Indicates whether a specific property on the current entity was changed and the changes were committed /// /// Name of the property to check /// True if Property was changed, otherwise False. Returns false if the entity had not been previously changed. public virtual bool WasPropertyDirty(string propertyName) { return WasDirty() && _lastPropertyChangedInfo.Any(x => x.Key == propertyName); } /// /// Resets the remembered dirty properties from before the last commit /// public void ForgetPreviouslyDirtyProperties() { _lastPropertyChangedInfo.Clear(); } /// /// Resets dirty properties by clearing the dictionary used to track changes. /// /// /// Please note that resetting the dirty properties could potentially /// obstruct the saving of a new or updated entity. /// public virtual void ResetDirtyProperties() { ResetDirtyProperties(true); } /// /// Resets dirty properties by clearing the dictionary used to track changes. /// /// /// true if we are to remember the last changes made after resetting /// /// /// Please note that resetting the dirty properties could potentially /// obstruct the saving of a new or updated entity. /// public virtual void ResetDirtyProperties(bool rememberPreviouslyChangedProperties) { if (rememberPreviouslyChangedProperties) { //copy the changed properties to the last changed properties _lastPropertyChangedInfo = _propertyChangedInfo.ToDictionary(v => v.Key, v => v.Value); } _propertyChangedInfo.Clear(); } /// /// Used by inheritors to set the value of properties, this will detect if the property value actually changed and if it did /// it will ensure that the property has a dirty flag set. /// /// /// /// /// returns true if the value changed /// /// This is required because we don't want a property to show up as "dirty" if the value is the same. For example, when we /// save a document type, nearly all properties are flagged as dirty just because we've 'reset' them, but they are all set /// to the same value, so it's really not dirty. /// internal bool SetPropertyValueAndDetectChanges(Func setValue, T value, PropertyInfo propertySelector) { var initVal = value; var newVal = setValue(value); if (!Equals(initVal, newVal)) { OnPropertyChanged(propertySelector); return true; } return false; } } }