PageRenderTime 43ms CodeModel.GetById 12ms app.highlight 25ms RepoModel.GetById 1ms app.codeStats 0ms

/Source/Bifrost/Sagas/Saga.cs

#
C# | 221 lines | 158 code | 38 blank | 25 comment | 11 complexity | 7f73e3219de92b21cad68790ed7731f6 MD5 | raw file
  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
 23using System;
 24using System.Collections.Generic;
 25using System.Linq;
 26using System.Reflection;
 27using Bifrost.Events;
 28using Bifrost.Extensions;
 29
 30namespace Bifrost.Sagas
 31{
 32    /// <summary>
 33    /// Represents a <see cref="ISaga"/>
 34    /// </summary>
 35    public class Saga : ISaga
 36    {
 37        readonly List<IChapter> _chapters = new List<IChapter>();
 38        readonly Dictionary<Guid, List<IEvent>> _aggregatedRootEvents = new Dictionary<Guid, List<IEvent>>();
 39
 40        /// <summary>
 41        /// Initializes a new instance of <see cref="Saga"/>
 42        /// </summary>
 43        public Saga()
 44        {
 45            Id = Guid.NewGuid();
 46            Partition = GetType().Name;
 47            Key = Guid.NewGuid().ToString();
 48            CurrentState = new SagaState();
 49            ChapterProperties = GetType().GetProperties().Where(p => p.PropertyType.HasInterface<IChapter>()).ToArray();
 50        }
 51
 52#pragma warning disable 1591 // Xml Comments
 53        public Guid Id { get; set; }
 54        public string Partition { get; set; }
 55        public string Key { get; set; }
 56
 57        public IEnumerable<IChapter> Chapters { get { return _chapters; } }
 58        public IChapter CurrentChapter { get; private set; }
 59
 60        public void SetCurrentChapter<T>() where T : IChapter
 61        {
 62            IChapter chapter = Get<T>();            
 63            SetCurrentChapter(chapter); 
 64        }
 65        
 66        public void SetCurrentChapter(IChapter chapter)
 67        {
 68            CurrentChapter = chapter;
 69            if (!Contains(chapter.GetType()))
 70                AddChapter(chapter);
 71            chapter.OnSetCurrent();
 72        }
 73        
 74
 75        public void AddChapter(IChapter chapter)
 76        {
 77            ThrowIfChapterAlreadyExist(chapter.GetType());
 78            _chapters.Add(chapter);
 79            SetChapterPropertyIfAny(chapter);
 80        }
 81
 82        public bool Contains<T>() where T : IChapter
 83        {
 84            return Contains(typeof(T));
 85        }
 86
 87        public bool Contains(Type type)
 88        {
 89            return _chapters.Any(s => s.GetType() == type);
 90        }
 91
 92        public T Get<T>() where T : IChapter
 93        {
 94            ThrowIfChapterDoesNotExist(typeof(T));
 95            return (T)_chapters.Where(s => s.GetType() == typeof (T)).Single();
 96        }
 97
 98        public PropertyInfo[] ChapterProperties { get; private set; }
 99
100
101        public IEnumerable<IEvent> GetUncommittedEvents()
102        {
103            var uncommittedEvents = new List<IEvent>();
104            foreach (var events in _aggregatedRootEvents.Values)
105                uncommittedEvents.AddRange(events);
106
107            return uncommittedEvents;
108        }
109
110        public void SetUncommittedEvents(IEnumerable<IEvent> events)
111        {
112            var query = events.GroupBy(e => e.EventSourceId).Select(g=>g);
113            foreach (var group in query)
114                _aggregatedRootEvents[group.Key] = group.ToList();
115        }
116
117        public void SaveUncommittedEventsToEventStore(IEventStore eventStore)
118        {
119            foreach( var aggregatedRootId in _aggregatedRootEvents.Keys )
120            {
121                var events = _aggregatedRootEvents[aggregatedRootId];
122
123                var uncommittedEventStream = new UncommittedEventStream(aggregatedRootId);
124                foreach (var @event in events)
125                    uncommittedEventStream.Append(@event);
126
127                eventStore.Save(uncommittedEventStream);
128            }
129        }
130
131        public CommittedEventStream Load(Type aggregatedRootType, Guid aggregateId)
132        {
133            var eventStream = new CommittedEventStream(aggregateId);
134            if( _aggregatedRootEvents.ContainsKey(aggregateId))
135                eventStream.Append(_aggregatedRootEvents[aggregateId]);
136            return eventStream;
137        }
138
139        public void Save(UncommittedEventStream eventsToSave)
140        {
141            if (!_aggregatedRootEvents.ContainsKey(eventsToSave.EventSourceId))
142                _aggregatedRootEvents[eventsToSave.EventSourceId] = new List<IEvent>();
143
144            _aggregatedRootEvents[eventsToSave.EventSourceId].AddRange(eventsToSave);
145        }
146
147        public EventSourceVersion GetLastCommittedVersion(Type aggregatedRootType, Guid aggregateId)
148        {
149            throw new NotImplementedException();
150        }
151
152        public SagaState CurrentState { get; set; }
153
154        public virtual void OnConclude()
155        {}
156
157        public virtual void OnBegin()
158        {}
159
160        public virtual void OnContinue()
161        {}
162
163        public void Begin()
164        {
165            CurrentState.TransitionTo(SagaState.BEGUN);
166            OnBegin();   
167        }
168
169        public void Continue()
170        {
171            CurrentState.TransitionTo(SagaState.CONTINUING);
172            OnContinue();
173        }
174
175        public void Conclude()
176        {
177            CurrentState.TransitionTo(SagaState.CONCLUDED);
178            OnConclude();
179        }
180
181        public bool IsNew
182        {
183            get { return CurrentState.IsNew; }
184        }
185        
186        public bool IsContinuing
187        {
188            get { return CurrentState.IsContinuing; }
189        }
190        
191        public bool IsBegun
192        {
193            get { return CurrentState.IsBegun; }
194        }
195        
196        public bool IsConcluded
197        {
198            get { return CurrentState.IsConcluded; }
199        }
200
201#pragma warning restore 1591 // Xml Comments
202        void SetChapterPropertyIfAny(IChapter chapter)
203        {
204            var property = ChapterProperties.Where(p => p.PropertyType.Equals(chapter.GetType())).SingleOrDefault();
205            if (property != null)
206                property.SetValue(this, chapter, null);
207        }
208
209        void ThrowIfChapterAlreadyExist(Type type)
210        {
211            if (_chapters.Any(s => s.GetType() == type))
212                throw new ChapterAlreadyExistException();
213        }
214
215        void ThrowIfChapterDoesNotExist(Type type)
216        {
217            if (!_chapters.Any(s => s.GetType() == type))
218                throw new ChapterDoesNotExistException();
219        }
220    }
221}