PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/Source/Bifrost/Sagas/Saga.cs

#
C# | 221 lines | 158 code | 38 blank | 25 comment | 11 complexity | 7f73e3219de92b21cad68790ed7731f6 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. #region License
  2. //
  3. // Copyright (c) 2008-2012, DoLittle Studios and Komplett ASA
  4. //
  5. // Licensed under the Microsoft Permissive License (Ms-PL), Version 1.1 (the "License")
  6. // With one exception :
  7. // Commercial libraries that is based partly or fully on Bifrost and is sold commercially,
  8. // must obtain a commercial license.
  9. //
  10. // You may not use this file except in compliance with the License.
  11. // You may obtain a copy of the license at
  12. //
  13. // http://bifrost.codeplex.com/license
  14. //
  15. // Unless required by applicable law or agreed to in writing, software
  16. // distributed under the License is distributed on an "AS IS" BASIS,
  17. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. // See the License for the specific language governing permissions and
  19. // limitations under the License.
  20. //
  21. #endregion
  22. using System;
  23. using System.Collections.Generic;
  24. using System.Linq;
  25. using System.Reflection;
  26. using Bifrost.Events;
  27. using Bifrost.Extensions;
  28. namespace Bifrost.Sagas
  29. {
  30. /// <summary>
  31. /// Represents a <see cref="ISaga"/>
  32. /// </summary>
  33. public class Saga : ISaga
  34. {
  35. readonly List<IChapter> _chapters = new List<IChapter>();
  36. readonly Dictionary<Guid, List<IEvent>> _aggregatedRootEvents = new Dictionary<Guid, List<IEvent>>();
  37. /// <summary>
  38. /// Initializes a new instance of <see cref="Saga"/>
  39. /// </summary>
  40. public Saga()
  41. {
  42. Id = Guid.NewGuid();
  43. Partition = GetType().Name;
  44. Key = Guid.NewGuid().ToString();
  45. CurrentState = new SagaState();
  46. ChapterProperties = GetType().GetProperties().Where(p => p.PropertyType.HasInterface<IChapter>()).ToArray();
  47. }
  48. #pragma warning disable 1591 // Xml Comments
  49. public Guid Id { get; set; }
  50. public string Partition { get; set; }
  51. public string Key { get; set; }
  52. public IEnumerable<IChapter> Chapters { get { return _chapters; } }
  53. public IChapter CurrentChapter { get; private set; }
  54. public void SetCurrentChapter<T>() where T : IChapter
  55. {
  56. IChapter chapter = Get<T>();
  57. SetCurrentChapter(chapter);
  58. }
  59. public void SetCurrentChapter(IChapter chapter)
  60. {
  61. CurrentChapter = chapter;
  62. if (!Contains(chapter.GetType()))
  63. AddChapter(chapter);
  64. chapter.OnSetCurrent();
  65. }
  66. public void AddChapter(IChapter chapter)
  67. {
  68. ThrowIfChapterAlreadyExist(chapter.GetType());
  69. _chapters.Add(chapter);
  70. SetChapterPropertyIfAny(chapter);
  71. }
  72. public bool Contains<T>() where T : IChapter
  73. {
  74. return Contains(typeof(T));
  75. }
  76. public bool Contains(Type type)
  77. {
  78. return _chapters.Any(s => s.GetType() == type);
  79. }
  80. public T Get<T>() where T : IChapter
  81. {
  82. ThrowIfChapterDoesNotExist(typeof(T));
  83. return (T)_chapters.Where(s => s.GetType() == typeof (T)).Single();
  84. }
  85. public PropertyInfo[] ChapterProperties { get; private set; }
  86. public IEnumerable<IEvent> GetUncommittedEvents()
  87. {
  88. var uncommittedEvents = new List<IEvent>();
  89. foreach (var events in _aggregatedRootEvents.Values)
  90. uncommittedEvents.AddRange(events);
  91. return uncommittedEvents;
  92. }
  93. public void SetUncommittedEvents(IEnumerable<IEvent> events)
  94. {
  95. var query = events.GroupBy(e => e.EventSourceId).Select(g=>g);
  96. foreach (var group in query)
  97. _aggregatedRootEvents[group.Key] = group.ToList();
  98. }
  99. public void SaveUncommittedEventsToEventStore(IEventStore eventStore)
  100. {
  101. foreach( var aggregatedRootId in _aggregatedRootEvents.Keys )
  102. {
  103. var events = _aggregatedRootEvents[aggregatedRootId];
  104. var uncommittedEventStream = new UncommittedEventStream(aggregatedRootId);
  105. foreach (var @event in events)
  106. uncommittedEventStream.Append(@event);
  107. eventStore.Save(uncommittedEventStream);
  108. }
  109. }
  110. public CommittedEventStream Load(Type aggregatedRootType, Guid aggregateId)
  111. {
  112. var eventStream = new CommittedEventStream(aggregateId);
  113. if( _aggregatedRootEvents.ContainsKey(aggregateId))
  114. eventStream.Append(_aggregatedRootEvents[aggregateId]);
  115. return eventStream;
  116. }
  117. public void Save(UncommittedEventStream eventsToSave)
  118. {
  119. if (!_aggregatedRootEvents.ContainsKey(eventsToSave.EventSourceId))
  120. _aggregatedRootEvents[eventsToSave.EventSourceId] = new List<IEvent>();
  121. _aggregatedRootEvents[eventsToSave.EventSourceId].AddRange(eventsToSave);
  122. }
  123. public EventSourceVersion GetLastCommittedVersion(Type aggregatedRootType, Guid aggregateId)
  124. {
  125. throw new NotImplementedException();
  126. }
  127. public SagaState CurrentState { get; set; }
  128. public virtual void OnConclude()
  129. {}
  130. public virtual void OnBegin()
  131. {}
  132. public virtual void OnContinue()
  133. {}
  134. public void Begin()
  135. {
  136. CurrentState.TransitionTo(SagaState.BEGUN);
  137. OnBegin();
  138. }
  139. public void Continue()
  140. {
  141. CurrentState.TransitionTo(SagaState.CONTINUING);
  142. OnContinue();
  143. }
  144. public void Conclude()
  145. {
  146. CurrentState.TransitionTo(SagaState.CONCLUDED);
  147. OnConclude();
  148. }
  149. public bool IsNew
  150. {
  151. get { return CurrentState.IsNew; }
  152. }
  153. public bool IsContinuing
  154. {
  155. get { return CurrentState.IsContinuing; }
  156. }
  157. public bool IsBegun
  158. {
  159. get { return CurrentState.IsBegun; }
  160. }
  161. public bool IsConcluded
  162. {
  163. get { return CurrentState.IsConcluded; }
  164. }
  165. #pragma warning restore 1591 // Xml Comments
  166. void SetChapterPropertyIfAny(IChapter chapter)
  167. {
  168. var property = ChapterProperties.Where(p => p.PropertyType.Equals(chapter.GetType())).SingleOrDefault();
  169. if (property != null)
  170. property.SetValue(this, chapter, null);
  171. }
  172. void ThrowIfChapterAlreadyExist(Type type)
  173. {
  174. if (_chapters.Any(s => s.GetType() == type))
  175. throw new ChapterAlreadyExistException();
  176. }
  177. void ThrowIfChapterDoesNotExist(Type type)
  178. {
  179. if (!_chapters.Any(s => s.GetType() == type))
  180. throw new ChapterDoesNotExistException();
  181. }
  182. }
  183. }