diff options
| author | Roy Ben-Shabat <Roy@Twine-s.com> | 2018-08-01 10:29:09 +0300 |
|---|---|---|
| committer | Roy Ben-Shabat <Roy@Twine-s.com> | 2018-08-01 10:29:09 +0300 |
| commit | d5c6067365e12674e95acaef4c4af45eaead8c3e (patch) | |
| tree | 0345d7e407a3e714061dc9bc26152c9efe977bd9 /Software/Visual_Studio | |
| parent | 91c672c0b0b01bc68a6adfa2aada337c6488a614 (diff) | |
| download | Tango-d5c6067365e12674e95acaef4c4af45eaead8c3e.tar.gz Tango-d5c6067365e12674e95acaef4c4af45eaead8c3e.zip | |
Implemented notification for observables entities modification..
Diffstat (limited to 'Software/Visual_Studio')
7 files changed, 258 insertions, 112 deletions
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Developer/ViewModels/MainViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Developer/ViewModels/MainViewVM.cs index a58757633..9ac78ccec 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Developer/ViewModels/MainViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Developer/ViewModels/MainViewVM.cs @@ -180,8 +180,8 @@ namespace Tango.MachineStudio.Developer.ViewModels if (_selectedMachine != null) { - _selectedMachine.Saved -= SelectedMachine_Saved; - _selectedMachine.Saved += SelectedMachine_Saved; + _selectedMachine.Modified -= SelectedMachine_Saved; + _selectedMachine.Modified += SelectedMachine_Saved; } } } diff --git a/Software/Visual_Studio/Tango.BL/IObservableEntity.cs b/Software/Visual_Studio/Tango.BL/IObservableEntity.cs index d83f3a6b2..ad112c4a1 100644 --- a/Software/Visual_Studio/Tango.BL/IObservableEntity.cs +++ b/Software/Visual_Studio/Tango.BL/IObservableEntity.cs @@ -17,9 +17,9 @@ namespace Tango.BL public interface IObservableEntity : INotifyDataErrorInfo, IParameterized { /// <summary> - /// Occurs after this observable has been saved. + /// Occurs after this observable has been modified and saved by this entity context or another. /// </summary> - event EventHandler Saved; + event EventHandler<ObservableModifiedEventArgs> Modified; /// <summary> /// Gets or sets the entity identifier. @@ -92,5 +92,10 @@ namespace Tango.BL /// <param name="context">The context.</param> /// <returns></returns> DbSet<T> GetDbSet<T>(ObservablesContext context) where T : class, IObservableEntity; + + /// <summary> + /// Raises the <see cref="Modified"/> event. + /// </summary> + void RaiseModified(ObservablesContext context, IObservableEntity source, IObservableEntity target); } } diff --git a/Software/Visual_Studio/Tango.BL/ObservableEntity.cs b/Software/Visual_Studio/Tango.BL/ObservableEntity.cs index 4f002c073..fc037f16a 100644 --- a/Software/Visual_Studio/Tango.BL/ObservableEntity.cs +++ b/Software/Visual_Studio/Tango.BL/ObservableEntity.cs @@ -26,6 +26,19 @@ using System.ComponentModel; namespace Tango.BL { + internal class ObservableEntitiesContainer + { + static ObservableEntitiesContainer() + { + RegisteredEntities = new List<IObservableEntity>(); + } + + /// <summary> + /// Gets the list of all registered entities ever created. + /// </summary> + internal static List<IObservableEntity> RegisteredEntities { get; private set; } + } + /// <summary> /// Represents a generic observable entity base class. /// </summary> @@ -35,13 +48,16 @@ namespace Tango.BL [Serializable] public abstract class ObservableEntity<T> : ExtendedObject, IObservableEntity where T : class, IObservableEntity { - private Regex regExDAL; - private List<KeyValuePair<String, String>> _currentErrors = new List<KeyValuePair<string, string>>(); //Holds the current validation errors. + #region Events /// <summary> - /// Occurs after this observable has been saved. + /// Occurs after this observable has been modified and saved by this entity context or another. /// </summary> - public event EventHandler Saved; + public event EventHandler<ObservableModifiedEventArgs> Modified; + + #endregion + + #region Properties private Int32 _id; /// <summary> @@ -117,19 +133,25 @@ namespace Tango.BL get { return GetType(); } } + #endregion + + #region Constructors + /// <summary> /// Initializes a new instance of the <see cref="ObservableEntity{T}"/> class. /// </summary> public ObservableEntity() { + ObservableEntitiesContainer.RegisteredEntities.Add(this); + Guid = System.Guid.NewGuid().ToString(); LastUpdated = DateTime.UtcNow; - regExDAL = new Regex(@" - (?<=[A-Z])(?=[A-Z][a-z]) | - (?<=[^A-Z])(?=[A-Z]) | - (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace); } + #endregion + + #region Public Methods + /// <summary> /// Saves the changes on this entity to database. /// </summary> @@ -155,7 +177,6 @@ namespace Tango.BL { context.SaveChanges(); } - OnSaved(); } /// <summary> @@ -246,6 +267,11 @@ Maybe you have deleted an entity that was no yet inserted into database?", LogCa }); } + /// <summary> + /// Gets the database set. + /// </summary> + /// <param name="context">The context.</param> + /// <returns></returns> public DbSet<T> GetDbSet(ObservablesContext context) { String tabelName = this.GetType().Name.PluralizeMVC(); @@ -269,12 +295,77 @@ Maybe you have deleted an entity that was no yet inserted into database?", LogCa return null; } - protected virtual void OnSaved() + /// <summary> + /// Gets the database set. + /// </summary> + /// <typeparam name="T1">The type of the 1.</typeparam> + /// <param name="context">The context.</param> + /// <returns></returns> + public DbSet<T1> GetDbSet<T1>(ObservablesContext context) where T1 : class, IObservableEntity { - Saved?.Invoke(this, new EventArgs()); + return GetDbSet(context) as DbSet<T1>; } - public static DbContext GetDbContextFromEntity(object entity) + /// <summary> + /// Clones this entity. + /// </summary> + /// <returns></returns> + public virtual T Clone() + { + var cloned = Activator.CreateInstance<T>(); + + foreach (var prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.SetMethod != null)) + { + if (!prop.PropertyType.IsGenericType) + { + prop.SetValue(cloned, prop.GetValue(this)); + } + } + + cloned.ID = 0; + cloned.Guid = System.Guid.NewGuid().ToString(); + cloned.LastUpdated = DateTime.UtcNow; + return cloned; + } + + /// <summary> + /// Compares another entity using a a JSON string comparison. + /// </summary> + /// <param name="entity">The entity.</param> + /// <returns></returns> + public bool CompareUsingJson(T entity) + { + String me = JsonConvert.SerializeObject(this); + String other = JsonConvert.SerializeObject(entity); + return me == other; + } + + /// <summary> + /// Deletes this entity using an SQL statement which will cause the database delete cascade effect. + /// </summary> + /// <param name="context"></param> + /// <returns></returns> + public Task DeleteCascadeAsync(ObservablesContext context) + { + return context.Database.ExecuteSqlCommandAsync(String.Format("DELETE FROM {0} WHERE GUID='{1}'", this.GetType().GetCustomAttribute<TableAttribute>().Name, Guid)); + } + + /// <summary> + /// Raises the <see cref="Modified" /> event. + /// </summary> + /// <param name="context"></param> + /// <param name="source"></param> + /// <param name="target"></param> + public void RaiseModified(ObservablesContext context, IObservableEntity source, IObservableEntity target) + { + Modified?.Invoke(this, new ObservableModifiedEventArgs(context, source, target)); + } + + #endregion + + #region Private Methods + + private static DbContext GetDbContextFromEntity(object entity) { var object_context = GetObjectContextFromEntity(entity); @@ -298,26 +389,52 @@ Maybe you have deleted an entity that was no yet inserted into database?", LogCa return context; } - public virtual T Clone() + #endregion + + #region Validation + + //Holds the current validation errors. + private List<KeyValuePair<String, String>> _currentErrors = new List<KeyValuePair<string, string>>(); + + /// <summary> + /// Occurs when the validation errors have changed for a property or for the entire entity. + /// </summary> + public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; + + private ObservableCollection<String> _validationErrors; + /// <summary> + /// Gets or sets the validation errors. + /// </summary> + [NotMapped] + [ParameterIgnore] + [XmlIgnore] + public ObservableCollection<String> ValidationErrors { - return (this as T).CloneEntity() as T; + get { return _validationErrors; } + protected set { _validationErrors = value; RaisePropertyChangedAuto(); } } - public bool CompareUsingJson(T entity) + private bool _validateOnPropertyChanged; + [NotMapped] + [ParameterIgnore] + [XmlIgnore] + public bool ValidateOnPropertyChanged { - String me = JsonConvert.SerializeObject(this); - String other = JsonConvert.SerializeObject(entity); - return me == other; + get { return _validateOnPropertyChanged; } + set { _validateOnPropertyChanged = value; RaisePropertyChangedAuto(); } } + private bool _hasErrors; /// <summary> - /// Deletes this entity using an SQL statement which will cause the database delete cascade effect. + /// Gets a value that indicates whether the entity has validation errors. /// </summary> - /// <param name="context"></param> - /// <returns></returns> - public Task DeleteCascadeAsync(ObservablesContext context) + [NotMapped] + [ParameterIgnore] + [XmlIgnore] + public bool HasErrors { - return context.Database.ExecuteSqlCommandAsync(String.Format("DELETE FROM {0} WHERE GUID='{1}'", this.GetType().GetCustomAttribute<TableAttribute>().Name, Guid)); + get { return _hasErrors; } + set { _hasErrors = value; RaisePropertyChangedAuto(); } } /// <summary> @@ -357,37 +474,36 @@ Maybe you have deleted an entity that was no yet inserted into database?", LogCa return !HasErrors; } - public DbSet<T1> GetDbSet<T1>(ObservablesContext context) where T1 : class, IObservableEntity - { - return GetDbSet(context) as DbSet<T1>; - } - + /// <summary> + /// Gets the validation errors for a specified property or for the entire entity. + /// </summary> + /// <param name="propertyName">The name of the property to retrieve validation errors for; or null or <see cref="F:System.String.Empty" />, to retrieve entity-level errors.</param> + /// <returns> + /// The validation errors for the property or entity. + /// </returns> public IEnumerable GetErrors(string propertyName) { return _currentErrors.Where(x => x.Key == propertyName).Select(x => x.Value).ToList(); } + /// <summary> + /// Called when entity is validating. + /// </summary> protected virtual void OnValidating(ObservablesContext context) { } + /// <summary> + /// Inserts the error. + /// </summary> + /// <param name="propName">Name of the property.</param> + /// <param name="error">The error.</param> protected void InsertError(String propName, String error) { _currentErrors.Add(new KeyValuePair<string, string>(propName, error)); } - private bool _validateOnPropertyChanged; - [NotMapped] - [ParameterIgnore] - [XmlIgnore] - public bool ValidateOnPropertyChanged - { - get { return _validateOnPropertyChanged; } - set { _validateOnPropertyChanged = value; RaisePropertyChangedAuto(); } - } - - /// <summary> /// Invoked the <see cref="ErrorsChanged"/> event. /// </summary> @@ -397,34 +513,14 @@ Maybe you have deleted an entity that was no yet inserted into database?", LogCa ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propName)); } - private bool _hasErrors; - /// <summary> - /// Gets a value that indicates whether the entity has validation errors. - /// </summary> - [NotMapped] - [ParameterIgnore] - [XmlIgnore] - public bool HasErrors - { - get { return _hasErrors; } - set { _hasErrors = value; RaisePropertyChangedAuto(); } - } + #endregion + + #region INotify Property Changed - private ObservableCollection<String> _validationErrors; /// <summary> - /// Gets or sets the validation errors. + /// Raises the property changed event. /// </summary> - [NotMapped] - [ParameterIgnore] - [XmlIgnore] - public ObservableCollection<String> ValidationErrors - { - get { return _validationErrors; } - protected set { _validationErrors = value; RaisePropertyChangedAuto(); } - } - - public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; - + /// <param name="propName">Name of the property.</param> protected override void RaisePropertyChanged(string propName) { base.RaisePropertyChanged(propName); @@ -435,6 +531,8 @@ Maybe you have deleted an entity that was no yet inserted into database?", LogCa } } + #endregion + #region Operator Overloading //public static bool operator ==(ObservableEntity<T> observable1, ObservableEntity<T> observable2) diff --git a/Software/Visual_Studio/Tango.BL/ObservableModifiedEventArgs.cs b/Software/Visual_Studio/Tango.BL/ObservableModifiedEventArgs.cs new file mode 100644 index 000000000..2b49154b3 --- /dev/null +++ b/Software/Visual_Studio/Tango.BL/ObservableModifiedEventArgs.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.BL +{ + /// <summary> + /// Represents an observable modified event arguments. + /// </summary> + /// <seealso cref="System.EventArgs" /> + public class ObservableModifiedEventArgs : EventArgs + { + /// <summary> + /// Gets or sets the modified entity context. + /// </summary> + public ObservablesContext Context { get; set; } + + /// <summary> + /// Gets or sets the modified entity. + /// </summary> + public IObservableEntity ModifiedEntity { get; set; } + + /// <summary> + /// Gets or sets the notified entity. + /// </summary> + public IObservableEntity NotifiedEntity { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="ObservableModifiedEventArgs"/> class. + /// </summary> + /// <param name="context">The context.</param> + /// <param name="source">The source.</param> + /// <param name="target">The target.</param> + public ObservableModifiedEventArgs(ObservablesContext context, IObservableEntity source, IObservableEntity target) + { + Context = context; + ModifiedEntity = source; + NotifiedEntity = target; + } + } +} diff --git a/Software/Visual_Studio/Tango.BL/ObservablesContextExtension.cs b/Software/Visual_Studio/Tango.BL/ObservablesContextExtension.cs index 1473845c5..7c3ade821 100644 --- a/Software/Visual_Studio/Tango.BL/ObservablesContextExtension.cs +++ b/Software/Visual_Studio/Tango.BL/ObservablesContextExtension.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Data.Entity; +using System.Data.Entity.Infrastructure; using System.Data.SQLite; using System.IO; using System.Linq; @@ -56,7 +57,8 @@ namespace Tango.BL /// <returns></returns> public static ObservablesContext CreateDefault() { - return CreateDefault(SettingsManager.Default.GetOrCreate<CoreSettings>().DataBaseSource); + var context = CreateDefault(SettingsManager.Default.GetOrCreate<CoreSettings>().DataBaseSource); + return context; } /// <summary> @@ -81,5 +83,39 @@ namespace Tango.BL return new ObservablesContext(source); } } + + /// <summary> + /// Saves all changes made in this context to the underlying database. + /// </summary> + /// <returns> + /// The number of objects written to the underlying database. + /// </returns> + public override int SaveChanges() + { + return base.SaveChanges(); + } + + /// <summary> + /// Extension point allowing the user to override the default behavior of validating only + /// added and modified entities. + /// </summary> + /// <param name="entityEntry">DbEntityEntry instance that is supposed to be validated.</param> + /// <returns> + /// true to proceed with validation; false otherwise. + /// </returns> + protected override bool ShouldValidateEntity(DbEntityEntry entityEntry) + { + if (entityEntry.State == EntityState.Modified && entityEntry.Entity is IObservableEntity) + { + IObservableEntity modified = entityEntry.Entity as IObservableEntity; + + Parallel.ForEach(ObservableEntitiesContainer.RegisteredEntities.Where(x => x.Guid == modified.Guid), (toNotify) => + { + toNotify.RaiseModified(this, modified, toNotify); + }); + } + + return base.ShouldValidateEntity(entityEntry); + } } } diff --git a/Software/Visual_Studio/Tango.BL/Tango.BL.csproj b/Software/Visual_Studio/Tango.BL/Tango.BL.csproj index 1553ab1f5..58767ea88 100644 --- a/Software/Visual_Studio/Tango.BL/Tango.BL.csproj +++ b/Software/Visual_Studio/Tango.BL/Tango.BL.csproj @@ -234,6 +234,7 @@ <Compile Include="ExtensionMethods\ProcessParametersTablesGroupExtensions.cs" /> <Compile Include="IObservableEntity.cs" /> <Compile Include="ObservableEntity.cs" /> + <Compile Include="ObservableModifiedEventArgs.cs" /> <Compile Include="ObservablesContext.cs" /> <Compile Include="ObservablesContextExtension.cs" /> <Compile Include="ObservablesEntitiesAdapter.cs" /> @@ -315,7 +316,7 @@ </Target> <ProjectExtensions> <VisualStudio> - <UserProperties BuildVersion_StartDate="2000/1/1" BuildVersion_UseGlobalSettings="False" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" /> + <UserProperties BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UseGlobalSettings="False" BuildVersion_StartDate="2000/1/1" /> </VisualStudio> </ProjectExtensions> </Project>
\ No newline at end of file diff --git a/Software/Visual_Studio/Tango.Core/ExtendedObject.cs b/Software/Visual_Studio/Tango.Core/ExtendedObject.cs index 03ac703ae..44663b503 100644 --- a/Software/Visual_Studio/Tango.Core/ExtendedObject.cs +++ b/Software/Visual_Studio/Tango.Core/ExtendedObject.cs @@ -23,16 +23,6 @@ namespace Tango.Core public class ExtendedObject : INotifyPropertyChanged { /// <summary> - /// Gets or sets a value indicating whether to globally disable property changed on extended objects. - /// </summary> - public static bool DisablePropertyChanged { get; set; } - - /// <summary> - /// Gets or sets a value indicating whether to globally disable UI invokations on extended objects. - /// </summary> - public static bool DisableUIInvokation { get; set; } - - /// <summary> /// Occurs when after InvalidateRelayCommands is called. /// </summary> [field: NonSerialized] @@ -70,10 +60,7 @@ namespace Tango.Core /// <param name="propName">Name of the property.</param> protected virtual void RaisePropertyChanged(String propName) { - if (!DisablePropertyChanged) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); - } + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); } /// <summary> @@ -82,10 +69,7 @@ namespace Tango.Core /// <param name="propName">Name of the property.</param> protected virtual void RaisePropertyChangedAuto([CallerMemberName] string caller = null) { - if (!DisablePropertyChanged) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller)); - } + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller)); } /// <summary> @@ -125,14 +109,7 @@ namespace Tango.Core /// <param name="action">The action.</param> protected virtual void InvokeUI(Action action) { - if (!DisableUIInvokation) - { - Application.Current.Dispatcher.BeginInvoke(action); - } - else - { - action(); - } + Application.Current.Dispatcher.BeginInvoke(action); } /// <summary> @@ -141,14 +118,7 @@ namespace Tango.Core /// <param name="action">The action.</param> protected virtual void InvokeUIOnIdle(Action action) { - if (!DisableUIInvokation) - { - Application.Current.Dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle); - } - else - { - action(); - } + Application.Current.Dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle); } /// <summary> @@ -157,14 +127,7 @@ namespace Tango.Core /// <param name="action">The action.</param> protected virtual void InvokeUINow(Action action) { - if (!DisableUIInvokation) - { - Application.Current.Dispatcher.Invoke(action); - } - else - { - action(); - } + Application.Current.Dispatcher.Invoke(action); } } } |
