/BlogEngine/DotNetSlave.BusinessLogic/BusinessBase.cs
C# | 669 lines | 315 code | 92 blank | 262 comment | 38 complexity | e56e345545aef8f8a9bb085e77962626 MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0, BSD-3-Clause
- namespace BlogEngine.Core
- {
- using System;
- using System.Collections.Generic;
- using System.Collections.Specialized;
- using System.ComponentModel;
- using System.Globalization;
- using System.Security;
- using System.Text;
- using System.Threading;
- using System.Web.Security;
-
- /// <summary>
- /// This is the base class from which most business objects will be derived.
- /// To create a business object, inherit from this class.
- /// </summary>
- /// <typeparam name="T">
- /// The type of the derived class.
- /// </typeparam>
- /// <typeparam name="TKey">
- /// The type of the Id property.
- /// </typeparam>
- [Serializable]
- public abstract class BusinessBase<T, TKey> : IDataErrorInfo, INotifyPropertyChanged, INotifyPropertyChanging, IChangeTracking, IDisposable
- where T : BusinessBase<T, TKey>, new()
- {
- #region Constants and Fields
-
- /// <summary>
- /// The broken rules.
- /// </summary>
- /// <remarks>
- /// This has been updated from using the old StringDictionary class from .Net 1.1. The StringDictionary class
- /// used case-insensitive keys, so this one needs to have StringComparer.OrdinalIgnoreCase passed in the constructor for
- /// backwards compatibility.
- ///
- /// INotifyPropertyChanging is implemented in case down the line someone wants to create a provider that uses BusinessBase
- /// objects as Linq-To-SQL entities(DataContext performance skyrockets when this is used).
- ///
- /// </remarks>
- private readonly Dictionary<String, String> brokenRules = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
-
- /// <summary>
- /// The changed properties.
- /// </summary>
- private readonly HashSet<string> changedProperties = new HashSet<string>();
-
- /// <summary>
- /// The date created.
- /// </summary>
- private DateTime dateCreated = DateTime.MinValue;
-
- /// <summary>
- /// The date modified.
- /// </summary>
- private DateTime dateModified = DateTime.MinValue;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="BusinessBase{T,TKey}"/> class.
- /// </summary>
- protected BusinessBase()
- {
- this.New = true;
- this.IsChanged = true;
- }
-
- #endregion
-
- #region Events
-
- /// <summary>
- /// Occurs when the class is Saved
- /// </summary>
- public static event EventHandler<SavedEventArgs> Saved;
-
- /// <summary>
- /// Occurs when the class is Saved
- /// </summary>
- public static event EventHandler<SavedEventArgs> Saving;
-
- /// <summary>
- /// Occurs when this instance is marked dirty.
- /// It means the instance has been changed but not saved.
- /// </summary>
- public event PropertyChangedEventHandler PropertyChanged;
-
- /// <summary>
- /// Event raised when an instance is about to change one of its property values.
- /// </summary>
- public event PropertyChangingEventHandler PropertyChanging;
-
- #endregion
-
- #region Properties
-
- /// <summary>
- /// Gets or sets the date on which the instance was created.
- /// </summary>
- public DateTime DateCreated
- {
- get
- {
- return this.dateCreated == DateTime.MinValue ? this.dateCreated : this.dateCreated.AddHours(BlogSettings.Instance.Timezone);
- }
-
- set
- {
- this.SetValue("DateCreated", value, ref this.dateCreated);
- }
- }
-
- /// <summary>
- /// Gets or sets the date on which the instance was modified.
- /// </summary>
- public DateTime DateModified
- {
- get
- {
- return (this.dateModified == DateTime.MinValue ? this.dateModified : this.dateModified.AddHours(BlogSettings.Instance.Timezone));
- }
-
- set
- {
- this.SetValue("DateModifier", value, ref this.dateModified);
- // this.dateModified = value;
- }
- }
-
- /// <summary>
- /// Gets an error message indicating what is wrong with this object.
- /// </summary>
- /// <returns>An error message indicating what is wrong with this object. The default is an empty string ("").</returns>
- public string Error
- {
- get
- {
- return this.ValidationMessage;
- }
- }
-
- /// <summary>
- /// Gets or sets the unique Identification of the object.
- /// </summary>
- public TKey Id { get; set; }
-
- /// <summary>
- /// Gets a value indicating whether if this object's data has been changed.
- /// </summary>
- public virtual bool IsChanged { get; private set; }
-
- /// <summary>
- /// Gets a value indicating whether if this object is marked for deletion.
- /// </summary>
- public bool Deleted { get; private set; }
-
- /// <summary>
- /// Gets a value indicating whether if this is a new object, False if it is a pre-existing object.
- /// </summary>
- public bool New { get; private set; }
-
- /// <summary>
- /// Gets a value indicating whether the object is valid or not.
- /// </summary>
- public bool Valid
- {
- get
- {
- this.ValidationRules();
- return this.brokenRules.Count == 0;
- }
- }
-
- /// ///
- /// <summary>
- /// Gets if the object has broken business rules, use this property to get access
- /// to the different validation messages.
- /// </summary>
- public virtual string ValidationMessage
- {
- get
- {
- if (!this.Valid)
- {
- var sb = new StringBuilder();
- foreach (string messages in this.brokenRules.Values)
- {
- sb.AppendLine(messages);
- }
-
- return sb.ToString();
- }
-
- return string.Empty;
- }
- }
-
- /// <summary>
- /// Gets a collection of the properties that have
- /// been marked as being dirty.
- /// </summary>
- protected virtual HashSet<string> ChangedProperties
- {
- get { return this.changedProperties; }
- }
-
- /// <summary>
- /// Gets whether or not the current user owns this object.
- /// </summary>
- /// <returns></returns>
- public virtual bool CurrentUserOwns
- {
- get { return false; }
- }
-
- /// <summary>
- /// Gets whether the current user can delete this object.
- /// </summary>
- /// <returns></returns>
- public virtual bool CanUserDelete
- {
- get { return Security.IsAdministrator; }
- }
-
- /// <summary>
- /// Gets whether the current user can edit this object.
- /// </summary>
- /// <returns></returns>
- public virtual bool CanUserEdit
- {
- get { return Security.IsAdministrator; }
- }
-
- /// <summary>
- /// Gets a value indicating whether the object has been disposed.
- /// <remarks>
- /// If the objects is disposed, it must not be disposed a second
- /// time. The Disposed property is set the first time the object
- /// is disposed. If the Disposed property is true, then the Dispose()
- /// method will not dispose again. This help not to prolong the object's
- /// life if the Garbage Collector.
- /// </remarks>
- /// </summary>
- /// <value>
- /// <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
- /// </value>
- protected bool Disposed { get; private set; }
-
- #endregion
-
- #region Indexers
-
- /// <summary>
- /// Gets the <see cref = "System.String" /> with the specified column name.
- /// </summary>
- /// <param name="columnName">The column name.</param>
- public string this[string columnName]
- {
- get
- {
- return this.brokenRules.ContainsKey(columnName) ? this.brokenRules[columnName] : string.Empty;
- }
- }
-
- #endregion
-
- #region Operators
-
- /// <summary>
- /// Checks to see if two business objects are the same.
- /// </summary>
- /// <param name="first">The first.</param>
- /// <param name="second">The second.</param>
- /// <returns>The result of the operator.</returns>
- public static bool operator ==(BusinessBase<T, TKey> first, BusinessBase<T, TKey> second)
- {
- return ReferenceEquals(first, second) ||
- ((object)first != null && (object)second != null && first.GetHashCode() == second.GetHashCode());
- }
-
- /// <summary>
- /// Checks to see if two business objects are different.
- /// </summary>
- /// <param name="first">The first.</param>
- /// <param name="second">The second.</param>
- /// <returns>The result of the operator.</returns>
- public static bool operator !=(BusinessBase<T, TKey> first, BusinessBase<T, TKey> second)
- {
- return !(first == second);
- }
-
- #endregion
-
- #region Public Methods
-
- /// <summary>
- /// Loads an instance of the object based on the Id.
- /// </summary>
- /// <param name="id">The unique identifier of the object</param>
- /// <returns>The instance of the object based on the Id.</returns>
- public static T Load(TKey id)
- {
- var instance = new T();
- instance = instance.DataSelect(id);
- instance.Id = id;
-
- instance.MarkOld();
- return instance;
- }
-
- /// <summary>
- /// Marks the object for deletion. It will then be
- /// deleted when the object's Save() method is called.
- /// </summary>
- public virtual void Delete()
- {
- this.Deleted = true;
- this.IsChanged = true;
- }
-
- /// <summary>
- /// Comapares this object with another
- /// </summary>
- /// <param name="obj">
- /// The object to compare
- /// </param>
- /// <returns>
- /// True if the two objects as equal
- /// </returns>
- public override bool Equals(object obj)
- {
- return obj != null && (obj.GetType() == this.GetType() && obj.GetHashCode() == this.GetHashCode());
- }
-
- /// <summary>
- /// A uniquely key to identify this particullar instance of the class
- /// </summary>
- /// <returns>
- /// A unique integer value
- /// </returns>
- public override int GetHashCode()
- {
- return this.Id.GetHashCode();
- }
-
- /// <summary>
- /// Marks the object as being an clean,
- /// which means not dirty.
- /// </summary>
- public virtual void MarkOld()
- {
- this.IsChanged = false;
- this.New = false;
- this.changedProperties.Clear();
- }
-
- /// <summary>
- /// Saves the object to the data store (inserts, updates or deletes).
- /// </summary>
- /// <returns>The SaveAction.</returns>
- public virtual SaveAction Save()
- {
- if (this.Deleted && !this.CanUserDelete)
- {
- throw new SecurityException("You are not authorized to delete the object");
- }
-
- if (!this.Valid && !this.Deleted)
- {
- throw new InvalidOperationException(this.ValidationMessage);
- }
-
- if (this.Disposed && !this.Deleted)
- {
- throw new InvalidOperationException(
- string.Format(CultureInfo.InvariantCulture, "You cannot save a disposed {0}", this.GetType().Name));
- }
-
- return this.IsChanged ? this.Update() : SaveAction.None;
- }
-
- #endregion
-
- #region Implemented Interfaces
-
- #region IChangeTracking
-
- /// <summary>
- /// Resets the object's state to unchanged by accepting the modifications.
- /// </summary>
- public void AcceptChanges()
- {
- this.Save();
- }
-
- #endregion
-
- #region IDisposable
-
- /// <summary>
- /// Disposes the object and frees ressources for the Garbage Collector.
- /// </summary>
- public void Dispose()
- {
- this.Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- #endregion
-
- #region "INotifyPropertyChanged"
-
- /// <summary>
- /// Marks an object as being dirty, or changed.
- /// </summary>
- /// <param name="propertyName">
- /// The name of the property to mark dirty.
- /// </param>
- protected virtual void MarkChanged(string propertyName)
- {
- this.IsChanged = true;
-
- // No need to check for duplicates since changedProperties
- // is just a HashSet.
- this.changedProperties.Add(propertyName);
- this.OnPropertyChanged(propertyName);
- }
-
- /// <summary>
- /// Raises the PropertyChanged event safely.
- /// </summary>
- /// <param name="propertyName">
- /// The property Name.
- /// </param>
- protected virtual void OnPropertyChanged(string propertyName)
- {
- if (this.PropertyChanged != null)
- {
- this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
- }
- }
-
- #endregion
-
- #region "INotifyPropertyChanging"
-
- /// <summary>
- /// Method called just before a property's value will change.
- /// </summary>
- /// <param name="propertyName">The name of the property whose value changing.</param>
- /// <remarks>
- ///
- /// This method should only be called when a value is definitely going to be changed. This
- /// should occur after any value validation or other methods are called.
- ///
- /// </remarks>
- protected virtual void OnPropertyChanging(string propertyName)
- {
- if (PropertyChanging != null)
- {
- PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
- }
- }
-
- #endregion
-
- #endregion
-
- #region Methods
-
- /// <summary>
- /// Raises the Saved event.
- /// </summary>
- /// <param name="businessObject">
- /// The business Object.
- /// </param>
- /// <param name="action">
- /// The action.
- /// </param>
- protected static void OnSaved(BusinessBase<T, TKey> businessObject, SaveAction action)
- {
- if (Saved != null)
- {
- Saved(businessObject, new SavedEventArgs(action));
- }
- }
-
- /// <summary>
- /// Raises the Saving event
- /// </summary>
- /// <param name="businessObject">
- /// The business Object.
- /// </param>
- /// <param name="action">
- /// The action.
- /// </param>
- protected static void OnSaving(BusinessBase<T, TKey> businessObject, SaveAction action)
- {
- if (Saving != null)
- {
- Saving(businessObject, new SavedEventArgs(action));
- }
- }
-
- /// <summary>
- /// Add or remove a broken rule.
- /// </summary>
- /// <param name="propertyName">
- /// The name of the property.
- /// </param>
- /// <param name="errorMessage">
- /// The description of the error
- /// </param>
- /// <param name="isbroken">
- /// True if the validation rule is broken.
- /// </param>
- protected virtual void AddRule(string propertyName, string errorMessage, bool isbroken)
- {
- if (isbroken)
- {
- this.brokenRules[propertyName] = errorMessage;
- }
- else
- {
- this.brokenRules.Remove(propertyName);
- }
- }
-
- /// <summary>
- /// Deletes the object from the data store.
- /// </summary>
- protected abstract void DataDelete();
-
- /// <summary>
- /// Inserts a new object to the data store.
- /// </summary>
- protected abstract void DataInsert();
-
- /// <summary>
- /// Retrieves the object from the data store and populates it.
- /// </summary>
- /// <param name="id">
- /// The unique identifier of the object.
- /// </param>
- /// <returns>
- /// True if the object exists and is being populated successfully
- /// </returns>
- protected abstract T DataSelect(TKey id);
-
- /// <summary>
- /// Updates the object in its data store.
- /// </summary>
- protected abstract void DataUpdate();
-
- /// <summary>
- /// Disposes the object and frees ressources for the Garbage Collector.
- /// </summary>
- /// <param name="disposing">
- /// If true, the object gets disposed.
- /// </param>
- protected virtual void Dispose(bool disposing)
- {
- try
- {
- if (this.Disposed)
- {
- return;
- }
-
- if (!disposing)
- {
- return;
- }
-
- this.changedProperties.Clear();
- this.brokenRules.Clear();
- }
- finally
- {
- this.Disposed = true;
- }
- }
-
-
-
- /// <summary>
- /// Use this method to change values of properties that participate in INotifyPropertyChanged event notification.
- /// </summary>
- /// <typeparam name="TValueType">The object type for both the new value and old value.</typeparam>
- /// <param name="propertyName">The name of the property that should be used when raising the PropertyChanged event.</param>
- /// <param name="newValue">The new value to be set on the property if it's different from oldValue</param>
- /// <param name="oldValue">The current value of the property.</param>
- /// <returns>True if the the property value has been changed, false otherwise.</returns>
- /// <remarks>
- ///
- /// This is left as virtual so users can override this if they have their own validation needs.
- ///
- /// </remarks>
- protected virtual bool SetValue<TValueType>(string propertyName, TValueType newValue, ref TValueType oldValue)
- {
- bool isChanged = (!Object.Equals(newValue, oldValue));
-
- if (isChanged)
- {
- OnPropertyChanging(propertyName);
- oldValue = newValue;
- MarkChanged(propertyName);
- }
- return isChanged;
-
- }
-
- /// <summary>
- /// Reinforces the business rules by adding additional rules to the
- /// broken rules collection.
- /// </summary>
- protected abstract void ValidationRules();
-
- /// <summary>
- /// Is called by the save method when the object is old and dirty.
- /// </summary>
- /// <returns>
- /// The update.
- /// </returns>
- private SaveAction Update()
- {
- var action = SaveAction.None;
-
- if (this.Deleted)
- {
- if (!this.New)
- {
- action = SaveAction.Delete;
- OnSaving(this, action);
- this.DataDelete();
- }
- }
- else
- {
- if (this.New)
- {
- if (this.dateCreated == DateTime.MinValue)
- {
- this.dateCreated = DateTime.Now;
- }
-
- this.dateModified = DateTime.Now;
- action = SaveAction.Insert;
- OnSaving(this, action);
- this.DataInsert();
- }
- else
- {
- this.dateModified = DateTime.Now;
-
- action = SaveAction.Update;
- OnSaving(this, action);
- this.DataUpdate();
- }
-
- this.MarkOld();
- }
-
- OnSaved(this, action);
- return action;
- }
-
- #endregion
- }
- }