1 //------------------------------------------------------------------------------
2 // <copyright file="Coordinator.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
9 using System.Collections.Generic;
10 using System.Diagnostics;
13 using System.Globalization;
14 using System.Data.Objects.Internal;
16 namespace System.Data.Common.Internal.Materialization
20 /// A coordinator is responsible for tracking state and processing result in a root or nested query
21 /// result collection. The coordinator exists within a graph, and knows its Parent, (First)Child,
22 /// and Next sibling. This allows the Shaper to use the coordinator as a simple state machine when
23 /// consuming store reader results.
25 internal abstract class Coordinator
30 /// The factory used to generate this coordinator instance. Contains delegates used
31 /// by the Shaper during result enumeration.
33 internal readonly CoordinatorFactory CoordinatorFactory;
36 /// Parent coordinator (the coordinator producing rows containing this collection).
37 /// If this is the root, null.
39 internal readonly Coordinator Parent;
42 /// First coordinator for nested results below this collection. When reading a new row
43 /// for this coordinator, we walk down to the Child.
45 /// NOTE:: this cannot be readonly because we can't know both the parent and the child
46 /// at initialization time; we set the Child in the parent's constructor.
48 public Coordinator Child
50 get { return _child; }
51 protected set { _child = value; }
53 private Coordinator _child;
56 /// Next coordinator at this depth. Once we're done consuming results for this reader,
57 /// we move on to this.Next.
59 internal readonly Coordinator Next;
62 /// Indicates whether data has been read for the collection being aggregated or yielded
63 /// by this coordinator.
67 get { return _isEntered; }
68 protected set { _isEntered = value; }
70 private bool _isEntered;
73 /// Indicates whether this is the top level coordinator for a query.
77 get { return null == Parent; }
84 protected Coordinator(CoordinatorFactory coordinatorFactory, Coordinator parent, Coordinator next)
86 this.CoordinatorFactory = coordinatorFactory;
93 #region "public" surface area
96 /// Registers this hierarchy of coordinators in the given shaper.
98 internal void Initialize(Shaper shaper)
100 ResetCollection(shaper);
102 // Add this coordinator to the appropriate state slot in the
103 // shaper so that it is available to materialization delegates.
104 shaper.State[this.CoordinatorFactory.StateSlot] = this;
106 if (null != this.Child)
108 this.Child.Initialize(shaper);
110 if (null != this.Next)
112 this.Next.Initialize(shaper);
117 /// Determines the maximum depth of this subtree.
119 internal int MaxDistanceToLeaf()
122 Coordinator child = this.Child;
123 while (null != child)
125 maxDistance = Math.Max(maxDistance, child.MaxDistanceToLeaf() + 1);
132 /// This method is called when the current collection is finished and it's time to move to the next collection.
133 /// Recursively initializes children and siblings as well.
135 internal abstract void ResetCollection(Shaper shaper);
138 /// Precondition: the current row has data for the coordinator.
139 /// Side-effects: updates keys currently stored in state and updates IsEntered if a new value is encountered.
140 /// Determines whether the row contains the next element in this collection.
142 internal bool HasNextElement(Shaper shaper)
144 // check if this row contains a new element for this coordinator
147 if (!this.IsEntered || !this.CoordinatorFactory.CheckKeys(shaper))
149 // remember initial keys values
150 this.CoordinatorFactory.SetKeys(shaper);
151 this.IsEntered = true;
159 /// Precondition: the current row has data and contains a new element for the coordinator.
160 /// Reads the next element in this collection.
162 internal abstract void ReadNextElement(Shaper shaper);
168 /// Typed <see cref="Coordinator"/>
170 internal class Coordinator<T> : Coordinator
174 internal readonly CoordinatorFactory<T> TypedCoordinatorFactory;
177 /// Exposes the Current element that has been materialized (and is being populated) by this coordinator.
181 get { return _current; }
186 /// For ObjectResult, aggregates all elements for in the nested collection handled by this coordinator.
188 private ICollection<T> _elements;
191 /// For ObjectResult, aggregates all elements as wrapped entities for in the nested collection handled by this coordinator.
193 private List<IEntityWrapper> _wrappedElements;
196 /// Delegate called when the current nested collection has been consumed. This is necessary in Span
197 /// scenarios where an EntityCollection RelatedEnd is populated only when all related entities have
198 /// been materialized. This version of the close handler works with wrapped entities.
200 private Action<Shaper, List<IEntityWrapper>> _handleClose;
203 /// For nested, object-layer coordinators we want to collect all the elements we find and handle them
204 /// when the root coordinator advances. Otherwise we just want to return them as we find them.
206 private readonly bool IsUsingElementCollection;
212 internal Coordinator(CoordinatorFactory<T> coordinator, Coordinator parent, Coordinator next)
213 : base(coordinator, parent, next)
215 this.TypedCoordinatorFactory = coordinator;
217 // generate all children
218 Coordinator nextChild = null;
219 foreach (var nestedCoordinator in coordinator.NestedCoordinators.Reverse())
221 // last child processed is first child...
222 this.Child = nestedCoordinator.CreateCoordinator(this, nextChild);
223 nextChild = this.Child;
226 this.IsUsingElementCollection = (!this.IsRoot && typeof(T) != typeof(RecordState));
231 #region "public" surface area
233 internal override void ResetCollection(Shaper shaper)
235 // Check to see if anyone has registered for notification when the current coordinator
237 if (null != _handleClose)
239 _handleClose(shaper, _wrappedElements);
243 // Reset is entered for this collection.
244 this.IsEntered = false;
246 if (IsUsingElementCollection)
248 _elements = this.TypedCoordinatorFactory.InitializeCollection(shaper);
249 _wrappedElements = new List<IEntityWrapper>();
252 if (null != this.Child)
254 this.Child.ResetCollection(shaper);
256 if (null != this.Next)
258 this.Next.ResetCollection(shaper);
262 internal override void ReadNextElement(Shaper shaper)
265 IEntityWrapper wrappedElement = null;
268 if (this.TypedCoordinatorFactory.WrappedElement == null)
270 element = this.TypedCoordinatorFactory.Element(shaper);
274 wrappedElement = this.TypedCoordinatorFactory.WrappedElement(shaper);
275 // This cast may throw, in which case it will be immediately caught
276 // and the error handling expression will be used to get the appropriate error message.
277 element = (T)wrappedElement.Entity;
282 if (EntityUtil.IsCatchableExceptionType(e))
284 // Some errors can occur while a close handler is registered. This clears
285 // out the handler so that ElementWithErrorHandling will report the correct
286 // error rather than asserting on the missing close handler.
287 ResetCollection(shaper);
288 // call a variation of the "Element" delegate with more detailed
289 // error handling (to produce a better exception message)
290 element = this.TypedCoordinatorFactory.ElementWithErrorHandling(shaper);
296 if (IsUsingElementCollection)
298 _elements.Add(element);
299 if (wrappedElement != null)
301 _wrappedElements.Add(wrappedElement);
311 /// Sets the delegate called when this collection is closed. This close handler works on
312 /// a collection of wrapped entities, rather than on the raw entity objects.
314 internal void RegisterCloseHandler(Action<Shaper, List<IEntityWrapper>> closeHandler)
316 Debug.Assert(null == _handleClose, "more than one handler for a collection close 'event'");
317 _handleClose = closeHandler;
321 /// Called when we're disposing the enumerator;
323 internal void SetCurrentToDefault()
325 _current = default(T);
330 #region runtime callable code
332 // Code in this section is called from the delegates produced by the Translator. It may
333 // not show up if you search using Find All References
336 /// Returns a handle to the element aggregator for this nested collection.
338 private IEnumerable<T> GetElements()