PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/source/library/Interlace.UserInterface/Utilities/EntityGraphIsDirtyWatcher.cs

https://bitbucket.org/VahidN/interlace
C# | 231 lines | 140 code | 35 blank | 56 comment | 25 complexity | 63c537521775e100dbd6ad557c85f06d MD5 | raw file
  1. #region Using Directives and Copyright Notice
  2. // Copyright (c) 2007-2010, Computer Consultancy Pty Ltd
  3. // All rights reserved.
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are met:
  7. // * Redistributions of source code must retain the above copyright
  8. // notice, this list of conditions and the following disclaimer.
  9. // * Redistributions in binary form must reproduce the above copyright
  10. // notice, this list of conditions and the following disclaimer in the
  11. // documentation and/or other materials provided with the distribution.
  12. // * Neither the name of the Computer Consultancy Pty Ltd nor the
  13. // names of its contributors may be used to endorse or promote products
  14. // derived from this software without specific prior written permission.
  15. //
  16. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  17. // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. // ARE DISCLAIMED. IN NO EVENT SHALL COMPUTER CONSULTANCY PTY LTD BE LIABLE
  20. // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21. // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  22. // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  23. // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  24. // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  25. // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  26. // DAMAGE.
  27. using System;
  28. using System.Collections.Generic;
  29. using System.ComponentModel;
  30. using System.Text;
  31. using SD.LLBLGen.Pro.ORMSupportClasses;
  32. #endregion
  33. namespace Interlace.Utilities
  34. {
  35. /// <summary>
  36. /// Binds to an entity and watches the IsDirty flag of the entity and all connected entities.
  37. /// </summary>
  38. /// <remarks>
  39. /// <para>The dirty watcher finds all related entities (even when cycles are present) of
  40. /// a root entity, following any number of relations. It then watches the entities
  41. /// to see if any have their IsDirty flag set, and any RemovedEntityTracker collections to
  42. /// see if they contain any entities.</para>
  43. /// </remarks>
  44. public class EntityGraphIsDirtyWatcher
  45. {
  46. IEntity2 _boundTo;
  47. IEnumerable<IEntity2> _boundToGraph;
  48. bool _cachedIsDirty;
  49. /// <summary>
  50. /// Initializes a new instance of the <see cref="EntityGraphIsDirtyWatcher"/> class.
  51. /// </summary>
  52. public EntityGraphIsDirtyWatcher()
  53. {
  54. _boundTo = null;
  55. }
  56. /// <summary>
  57. /// Gets or sets the root object of of object tree that is being watched.
  58. /// </summary>
  59. /// <value>The bound to root object.</value>
  60. public IEntity2 BoundTo
  61. {
  62. get { return _boundTo; }
  63. set
  64. {
  65. if (value == _boundTo) return;
  66. if (_boundTo != null)
  67. {
  68. UnhookEvents();
  69. _boundToGraph = null;
  70. }
  71. _boundTo = value;
  72. if (_boundTo != null)
  73. {
  74. _boundToGraph = EntityGraphUtilities.GetAllEntitiesInGraph(_boundTo);
  75. HookEvents();
  76. }
  77. if (BoundToChanged != null) BoundToChanged(this, EventArgs.Empty);
  78. UpdateIsDirty();
  79. }
  80. }
  81. /// <summary>
  82. /// Occurs when the bound to object is changed.
  83. /// </summary>
  84. public event EventHandler BoundToChanged;
  85. void HookEvents()
  86. {
  87. foreach (IEntity2 entity in _boundToGraph)
  88. {
  89. entity.EntityContentsChanged += new EventHandler(entity_EntityContentsChanged);
  90. entity.PropertyChanged += new PropertyChangedEventHandler(entity_PropertyChanged);
  91. entity.AfterSave += new EventHandler(entity_AfterSave);
  92. foreach (IEntityCollection2 collection in entity.GetMemberEntityCollections())
  93. {
  94. collection.ListChanged += new ListChangedEventHandler(collection_ListChanged);
  95. if (collection.RemovedEntitiesTracker != null)
  96. {
  97. collection.RemovedEntitiesTracker.ListChanged +=
  98. new ListChangedEventHandler(RemovedEntitiesTracker_ListChanged);
  99. }
  100. }
  101. }
  102. }
  103. void entity_PropertyChanged(object sender, PropertyChangedEventArgs e)
  104. {
  105. IEntity2 senderEntity = sender as IEntity2;
  106. foreach (IEntityRelation relation in senderEntity.GetAllRelations())
  107. {
  108. if (relation.MappedFieldName == e.PropertyName)
  109. {
  110. // If they've updated a 1:1 related entity, then the graph needs to
  111. // be updated to make sure we're not watching the old entity
  112. UnhookEvents();
  113. _boundToGraph = EntityGraphUtilities.GetAllEntitiesInGraph(_boundTo);
  114. HookEvents();
  115. // We've updated the graph, so no need to keep looking through the relations.
  116. break;
  117. }
  118. }
  119. UpdateIsDirty();
  120. }
  121. void RemovedEntitiesTracker_ListChanged(object sender, ListChangedEventArgs e)
  122. {
  123. UpdateIsDirty();
  124. }
  125. void collection_ListChanged(object sender, ListChangedEventArgs e)
  126. {
  127. if (e.ListChangedType != ListChangedType.ItemChanged)
  128. {
  129. UnhookEvents();
  130. _boundToGraph = EntityGraphUtilities.GetAllEntitiesInGraph(_boundTo);
  131. HookEvents();
  132. UpdateIsDirty();
  133. }
  134. }
  135. void UnhookEvents()
  136. {
  137. foreach (IEntity2 entity in _boundToGraph)
  138. {
  139. foreach (IEntityCollection2 collection in entity.GetMemberEntityCollections())
  140. {
  141. collection.ListChanged -= new ListChangedEventHandler(collection_ListChanged);
  142. if (collection.RemovedEntitiesTracker != null)
  143. {
  144. collection.RemovedEntitiesTracker.ListChanged +=
  145. new ListChangedEventHandler(RemovedEntitiesTracker_ListChanged);
  146. }
  147. }
  148. entity.EntityContentsChanged -= new EventHandler(entity_EntityContentsChanged);
  149. entity.PropertyChanged -= new PropertyChangedEventHandler(entity_PropertyChanged);
  150. entity.AfterSave -= new EventHandler(entity_AfterSave);
  151. }
  152. }
  153. void entity_EntityContentsChanged(object sender, EventArgs e)
  154. {
  155. UpdateIsDirty();
  156. }
  157. void entity_AfterSave(object sender, EventArgs e)
  158. {
  159. UpdateIsDirty();
  160. }
  161. void UpdateIsDirty()
  162. {
  163. _cachedIsDirty = false;
  164. if (_boundToGraph != null)
  165. {
  166. foreach (IEntity2 entity in _boundToGraph)
  167. {
  168. _cachedIsDirty = _cachedIsDirty || entity.IsDirty;
  169. foreach (IEntityCollection2 collection in entity.GetMemberEntityCollections())
  170. {
  171. if (collection.RemovedEntitiesTracker != null)
  172. {
  173. _cachedIsDirty = _cachedIsDirty || (collection.RemovedEntitiesTracker.Count > 0);
  174. }
  175. }
  176. // Optimization: If it's dirty, there's no reason to continue through the loop. The
  177. // graph is dirty.
  178. if (_cachedIsDirty) break;
  179. }
  180. }
  181. if (IsDirtyChanged != null) IsDirtyChanged(this, EventArgs.Empty);
  182. }
  183. /// <summary>
  184. /// Gets a value indicating whether any objects in the bound to object tree are dirty.
  185. /// </summary>
  186. /// <value><c>true</c> if there are dirty entities; otherwise, <c>false</c>.</value>
  187. public bool IsDirty
  188. {
  189. get { return _cachedIsDirty; }
  190. }
  191. /// <summary>
  192. /// Occurs when the IsDirty value changes.
  193. /// </summary>
  194. public event EventHandler IsDirtyChanged;
  195. }
  196. }