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