1 //------------------------------------------------------------------------------
2 // <copyright file="CoordinatorFactory.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
8 
9 using System.Collections.Generic;
10 using System.Diagnostics;
11 using System.Linq;
12 using System.Linq.Expressions;
13 using System.Text;
14 using System.Data.Objects.Internal;
15 
16 namespace System.Data.Common.Internal.Materialization
17 {
18     /// <summary>
19     /// An immutable class used to generate new coordinators. These coordinators are used
20     /// at runtime to materialize results.
21     /// </summary>
22     internal abstract class CoordinatorFactory
23     {
24         #region statics
25 
26         /// <summary>
27         /// Function of shaper that returns true; one default case when there is no explicit predicate.
28         /// </summary>
29         private static readonly Func<Shaper, bool> AlwaysTrue = s => true;
30 
31         /// <summary>
32         /// Function of shaper that returns false; one default case used when there is no explicit predicate.
33         /// </summary>
34         private static readonly Func<Shaper, bool> AlwaysFalse = s => false;
35 
36         #endregion
37 
38         #region state
39 
40         /// <summary>
41         /// Gets depth of the reader (0 is top-level -- which incidentally doesn't
42         /// require a coordinator...
43         /// </summary>
44         internal readonly int Depth;
45 
46         /// <summary>
47         /// Indicates which state slot in the Shaper.State is expected to hold the
48         /// value for this nested reader result.
49         /// </summary>
50         internal readonly int StateSlot;
51 
52         /// <summary>
53         /// A function determining whether the current row has data for this nested result.
54         /// </summary>
55         internal readonly Func<Shaper, bool> HasData;
56 
57         /// <summary>
58         /// A function setting key values. (the return value is irrelevant)
59         /// </summary>
60         internal readonly Func<Shaper, bool> SetKeys;
61 
62         /// <summary>
63         /// A function returning true if key values match the previously set values.
64         /// </summary>
65         internal readonly Func<Shaper, bool> CheckKeys;
66 
67         /// <summary>
68         /// Nested results below this (at depth + 1)
69         /// </summary>
70         internal readonly System.Collections.ObjectModel.ReadOnlyCollection<CoordinatorFactory> NestedCoordinators;
71 
72         /// <summary>
73         /// Indicates whether this is a leaf reader.
74         /// </summary>
75         internal readonly bool IsLeafResult;
76 
77         /// <summary>
78         /// Indicates whether this coordinator can be managed by a simple enumerator. A simple enumerator
79         /// returns a single element per row, so the following conditions disqualify the enumerator:
80         /// nested collections, data discriminators (not all rows have data), keys (not all rows have new data).
81         /// </summary>
82         internal readonly bool IsSimple;
83 
84         /// <summary>
85         /// For value-layer queries, the factories for all the records that we can potentially process
86         /// at this level in the query result.
87         /// </summary>
88         internal readonly System.Collections.ObjectModel.ReadOnlyCollection<RecordStateFactory> RecordStateFactories;
89 
90         #endregion
91 
92         #region constructor
93 
CoordinatorFactory(int depth, int stateSlot, Func<Shaper, bool> hasData, Func<Shaper, bool> setKeys, Func<Shaper, bool> checkKeys, CoordinatorFactory[] nestedCoordinators, RecordStateFactory[] recordStateFactories)94         protected CoordinatorFactory(int depth, int stateSlot, Func<Shaper, bool> hasData, Func<Shaper, bool> setKeys, Func<Shaper, bool> checkKeys, CoordinatorFactory[] nestedCoordinators, RecordStateFactory[] recordStateFactories)
95         {
96             this.Depth = depth;
97             this.StateSlot = stateSlot;
98 
99             // figure out if there are any nested coordinators
100             this.IsLeafResult = 0 == nestedCoordinators.Length;
101 
102             // if there is no explicit 'has data' discriminator, it means all rows contain data for the coordinator
103             if (hasData == null)
104             {
105                 this.HasData = AlwaysTrue;
106             }
107             else
108             {
109                 this.HasData = hasData;
110             }
111 
112             // if there is no explicit set key delegate, just return true (the value is not used anyways)
113             if (setKeys == null)
114             {
115                 this.SetKeys = AlwaysTrue;
116             }
117             else
118             {
119                 this.SetKeys = setKeys;
120             }
121 
122             // If there are no keys, it means different things depending on whether we are a leaf
123             // coordinator or an inner (or 'driving') coordinator. For a leaf coordinator, it means
124             // that every row is a new result. For an inner coordinator, it means that there is no
125             // key to check. This should only occur where there is a SingleRowTable (in other words,
126             // all rows are elements of a single child collection).
127             if (checkKeys == null)
128             {
129                 if (this.IsLeafResult)
130                 {
131                     this.CheckKeys = AlwaysFalse; // every row is a new result (the keys don't match)
132                 }
133                 else
134                 {
135                     this.CheckKeys = AlwaysTrue; // every row belongs to a single child collection
136                 }
137             }
138             else
139             {
140                 this.CheckKeys = checkKeys;
141             }
142             this.NestedCoordinators = new System.Collections.ObjectModel.ReadOnlyCollection<CoordinatorFactory>(nestedCoordinators);
143             this.RecordStateFactories = new System.Collections.ObjectModel.ReadOnlyCollection<RecordStateFactory>(recordStateFactories);
144 
145             // Determines whether this coordinator can be handled by a 'simple' enumerator. See IsSimple for details.
146             this.IsSimple = IsLeafResult && null == checkKeys && null == hasData;
147         }
148 
149         #endregion
150 
151         #region "public" surface area
152 
153         /// <summary>
154         /// Creates a buffer handling state needed by this coordinator.
155         /// </summary>
CreateCoordinator(Coordinator parent, Coordinator next)156         internal abstract Coordinator CreateCoordinator(Coordinator parent, Coordinator next);
157 
158         #endregion
159     }
160 
161     /// <summary>
162     /// Typed <see cref="CoordinatorFactory"/>
163     /// </summary>
164     internal sealed class CoordinatorFactory<TElement> : CoordinatorFactory
165     {
166         #region state
167 
168         /// <summary>
169         /// Reads a single element of the result from the given reader state object, returning the
170         /// result as a wrapped entity.  May be null if the element is not available as a wrapped entity.
171         /// </summary>
172         internal readonly Func<Shaper, IEntityWrapper> WrappedElement;
173 
174         /// <summary>
175         /// Reads a single element of the result from the given reader state object.
176         /// May be null if the element is available as a wrapped entity instead.
177         /// </summary>
178         internal readonly Func<Shaper, TElement> Element;
179 
180         /// <summary>
181         /// Same as Element but uses slower patterns to provide better exception messages (e.g.
182         /// using reader.GetValue + type check rather than reader.GetInt32)
183         /// </summary>
184         internal readonly Func<Shaper, TElement> ElementWithErrorHandling;
185 
186         /// <summary>
187         /// Initializes the collection storing results from this coordinator.
188         /// </summary>
189         internal readonly Func<Shaper, ICollection<TElement>> InitializeCollection;
190 
191         /// <summary>
192         /// Description of this CoordinatorFactory, used for debugging only; while this is not
193         /// needed in retail code, it is pretty important because it's the only description we'll
194         /// have once we compile the Expressions; debugging a problem with retail bits would be
195         /// pretty hard without this.
196         /// </summary>
197         private readonly string Description;
198 
199         #endregion
200 
201         #region constructor
202 
CoordinatorFactory(int depth, int stateSlot, Expression hasData, Expression setKeys, Expression checkKeys, CoordinatorFactory[] nestedCoordinators, Expression element, Expression elementWithErrorHandling, Expression initializeCollection, RecordStateFactory[] recordStateFactories)203         public CoordinatorFactory(int depth, int stateSlot, Expression hasData, Expression setKeys, Expression checkKeys, CoordinatorFactory[] nestedCoordinators, Expression element, Expression elementWithErrorHandling, Expression initializeCollection, RecordStateFactory[] recordStateFactories)
204             : base(depth, stateSlot, CompilePredicate(hasData), CompilePredicate(setKeys), CompilePredicate(checkKeys), nestedCoordinators, recordStateFactories)
205         {
206             // If we are in a case where a wrapped entity is available, then use it; otherwise use the raw element.
207             // However, in both cases, use the raw element for the error handling case where what we care about is
208             // getting the appropriate exception message.
209             if (typeof(IEntityWrapper).IsAssignableFrom(element.Type))
210             {
211                 this.WrappedElement = Translator.Compile<IEntityWrapper>(element);
212                 elementWithErrorHandling = Translator.Emit_UnwrapAndEnsureType(elementWithErrorHandling, typeof(TElement));
213             }
214             else
215             {
216                 this.Element = Translator.Compile<TElement>(element);
217             }
218             this.ElementWithErrorHandling = Translator.Compile<TElement>(elementWithErrorHandling);
219             this.InitializeCollection = null == initializeCollection
220                 ? s => new List<TElement>()
221                 : Translator.Compile<ICollection<TElement>>(initializeCollection);
222 
223             this.Description = new StringBuilder()
224                                     .Append("HasData: ")
225                                     .AppendLine(DescribeExpression(hasData))
226                                     .Append("SetKeys: ")
227                                     .AppendLine(DescribeExpression(setKeys))
228                                     .Append("CheckKeys: ")
229                                     .AppendLine(DescribeExpression(checkKeys))
230                                     .Append("Element: ")
231                                     .AppendLine(DescribeExpression(element))
232                                     .Append("ElementWithExceptionHandling: ")
233                                     .AppendLine(DescribeExpression(elementWithErrorHandling))
234                                     .Append("InitializeCollection: ")
235                                     .AppendLine(DescribeExpression(initializeCollection))
236                                     .ToString();
237         }
238 
239         #endregion
240 
241         #region expression helpers
242 
243         /// <summary>
244         /// Return the compiled expression for the predicate
245         /// </summary>
CompilePredicate(Expression predicate)246         private static Func<Shaper, bool> CompilePredicate(Expression predicate)
247         {
248             Func<Shaper, bool> result;
249             if (null == predicate)
250             {
251                 result = null;
252             }
253             else
254             {
255                 result = Translator.Compile<bool>(predicate);
256             }
257             return result;
258         }
259 
260         /// <summary>
261         /// Returns a string representation of the expression
262         /// </summary>
DescribeExpression(Expression expression)263         private static string DescribeExpression(Expression expression)
264         {
265             string result;
266             if (null == expression)
267             {
268                 result = "undefined";
269             }
270             else
271             {
272                 result = expression.ToString();
273             }
274             return result;
275         }
276 
277         #endregion
278 
279         #region "public" surface area
280 
281         /// <summary>
282         /// Create a coordinator used for materialization of collections. Unlike the CoordinatorFactory,
283         /// the Coordinator contains mutable state.
284         /// </summary>
CreateCoordinator(Coordinator parent, Coordinator next)285         internal override Coordinator CreateCoordinator(Coordinator parent, Coordinator next)
286         {
287             return new Coordinator<TElement>(this, parent, next);
288         }
289 
290         /// <summary>
291         /// Returns the "default" record state (that is, the one we use for PreRead/PastEnd reader states
292         /// </summary>
GetDefaultRecordState(Shaper<RecordState> shaper)293         internal RecordState GetDefaultRecordState(Shaper<RecordState> shaper)
294         {
295             RecordState result = null;
296             if (this.RecordStateFactories.Count > 0)
297             {
298                 //
299 
300                 result = (RecordState)shaper.State[this.RecordStateFactories[0].StateSlotNumber];
301                 Debug.Assert(null != result, "did you initialize the record states?");
302                 result.ResetToDefaultState();
303             }
304             return result;
305         }
306 
ToString()307         public override string ToString()
308         {
309             return Description;
310         }
311 
312         #endregion
313     }
314 }
315