PageRenderTime 26ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/src/EntityFramework/Core/Mapping/ViewGeneration/Structures/LeafCellTreeNode.cs

http://entityframework.codeplex.com
C# | 320 lines | 251 code | 33 blank | 36 comment | 33 complexity | 3f6320c9fd4bf36d542db626072a6d8d MD5 | raw file
Possible License(s): Apache-2.0
  1. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
  2. namespace System.Data.Entity.Core.Mapping.ViewGeneration.Structures
  3. {
  4. using System.Collections.Generic;
  5. using System.Data.Entity.Core.Common.Utils;
  6. using System.Data.Entity.Core.Mapping.ViewGeneration.CqlGeneration;
  7. using System.Data.Entity.Core.Mapping.ViewGeneration.QueryRewriting;
  8. using System.Data.Entity.Core.Metadata.Edm;
  9. using System.Data.Entity.Utilities;
  10. using System.Diagnostics;
  11. using System.Linq;
  12. using System.Text;
  13. // This class represents the nodes that reside at the leaves of the tree
  14. internal class LeafCellTreeNode : CellTreeNode
  15. {
  16. // effects: Encapsulate the cell wrapper in the node
  17. internal LeafCellTreeNode(ViewgenContext context, LeftCellWrapper cellWrapper)
  18. : base(context)
  19. {
  20. m_cellWrapper = cellWrapper;
  21. cellWrapper.AssertHasUniqueCell();
  22. m_rightFragmentQuery = FragmentQuery.Create(
  23. cellWrapper.OriginalCellNumberString,
  24. cellWrapper.CreateRoleBoolean(),
  25. cellWrapper.RightCellQuery);
  26. }
  27. internal LeafCellTreeNode(ViewgenContext context, LeftCellWrapper cellWrapper, FragmentQuery rightFragmentQuery)
  28. : base(context)
  29. {
  30. m_cellWrapper = cellWrapper;
  31. m_rightFragmentQuery = rightFragmentQuery;
  32. }
  33. internal static readonly IEqualityComparer<LeafCellTreeNode> EqualityComparer = new LeafCellTreeNodeComparer();
  34. // The cell at the leaf level
  35. private readonly LeftCellWrapper m_cellWrapper;
  36. private readonly FragmentQuery m_rightFragmentQuery;
  37. internal LeftCellWrapper LeftCellWrapper
  38. {
  39. get { return m_cellWrapper; }
  40. }
  41. internal override MemberDomainMap RightDomainMap
  42. {
  43. get { return m_cellWrapper.RightDomainMap; }
  44. }
  45. // effects: See CellTreeNode.FragmentQuery
  46. internal override FragmentQuery LeftFragmentQuery
  47. {
  48. get { return m_cellWrapper.FragmentQuery; }
  49. }
  50. internal override FragmentQuery RightFragmentQuery
  51. {
  52. get
  53. {
  54. Debug.Assert(m_rightFragmentQuery != null, "Unassigned right fragment query");
  55. return m_rightFragmentQuery;
  56. }
  57. }
  58. // effects: See CellTreeNode.Attributes
  59. internal override Set<MemberPath> Attributes
  60. {
  61. get { return m_cellWrapper.Attributes; }
  62. }
  63. // effects: See CellTreeNode.Children
  64. internal override List<CellTreeNode> Children
  65. {
  66. get { return new List<CellTreeNode>(); }
  67. }
  68. // effects: See CellTreeNode.OpType
  69. internal override CellTreeOpType OpType
  70. {
  71. get { return CellTreeOpType.Leaf; }
  72. }
  73. internal override int NumProjectedSlots
  74. {
  75. get { return LeftCellWrapper.RightCellQuery.NumProjectedSlots; }
  76. }
  77. internal override int NumBoolSlots
  78. {
  79. get { return LeftCellWrapper.RightCellQuery.NumBoolVars; }
  80. }
  81. internal override TOutput Accept<TInput, TOutput>(CellTreeVisitor<TInput, TOutput> visitor, TInput param)
  82. {
  83. return visitor.VisitLeaf(this, param);
  84. }
  85. internal override TOutput Accept<TInput, TOutput>(SimpleCellTreeVisitor<TInput, TOutput> visitor, TInput param)
  86. {
  87. return visitor.VisitLeaf(this, param);
  88. }
  89. internal override bool IsProjectedSlot(int slot)
  90. {
  91. var cellQuery = LeftCellWrapper.RightCellQuery;
  92. if (IsBoolSlot(slot))
  93. {
  94. return cellQuery.GetBoolVar(SlotToBoolIndex(slot)) != null;
  95. }
  96. else
  97. {
  98. return cellQuery.ProjectedSlotAt(slot) != null;
  99. }
  100. }
  101. internal override CqlBlock ToCqlBlock(
  102. bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum, ref List<WithRelationship> withRelationships)
  103. {
  104. // Get the projected slots and the boolean expressions
  105. var totalSlots = requiredSlots.Length;
  106. var cellQuery = LeftCellWrapper.RightCellQuery;
  107. var projectedSlots = new SlotInfo[totalSlots];
  108. Debug.Assert(
  109. cellQuery.NumProjectedSlots + cellQuery.NumBoolVars == totalSlots,
  110. "Wrong number of projected slots in node");
  111. Debug.Assert(
  112. cellQuery.NumProjectedSlots == ProjectedSlotMap.Count,
  113. "Different number of slots in cell query and what we have mappings for");
  114. // Add the regular fields
  115. for (var i = 0; i < cellQuery.NumProjectedSlots; i++)
  116. {
  117. var slot = cellQuery.ProjectedSlotAt(i);
  118. // If the slot is not null, we will project it
  119. // For extents, we say that all requiredlots are the only the
  120. // ones that are CLR non-null. Recall that "real" nulls are
  121. // handled by having a CellConstant.Null in ConstantSlot
  122. if (requiredSlots[i]
  123. && slot == null)
  124. {
  125. var memberPath = ProjectedSlotMap[i];
  126. var defaultValue =
  127. new ConstantProjectedSlot(Domain.GetDefaultValueForMemberPath(memberPath, GetLeaves(), ViewgenContext.Config));
  128. cellQuery.FixMissingSlotAsDefaultConstant(i, defaultValue);
  129. slot = defaultValue;
  130. }
  131. var slotInfo = new SlotInfo(
  132. requiredSlots[i], slot != null,
  133. slot, ProjectedSlotMap[i]);
  134. projectedSlots[i] = slotInfo;
  135. }
  136. // Add the boolean fields
  137. for (var boolNum = 0; boolNum < cellQuery.NumBoolVars; boolNum++)
  138. {
  139. var expr = cellQuery.GetBoolVar(boolNum);
  140. BooleanProjectedSlot boolSlot;
  141. if (expr != null)
  142. {
  143. boolSlot = new BooleanProjectedSlot(expr, identifiers, boolNum);
  144. }
  145. else
  146. {
  147. boolSlot = new BooleanProjectedSlot(BoolExpression.False, identifiers, boolNum);
  148. }
  149. var slotIndex = BoolIndexToSlot(boolNum);
  150. var slotInfo = new SlotInfo(
  151. requiredSlots[slotIndex], expr != null,
  152. boolSlot, null);
  153. projectedSlots[slotIndex] = slotInfo;
  154. }
  155. // See if we are generating a query view and whether there are any colocated foreign keys for which
  156. // we have to add With statements.
  157. IEnumerable<SlotInfo> totalProjectedSlots = projectedSlots;
  158. if ((cellQuery.Extent.EntityContainer.DataSpace == DataSpace.SSpace)
  159. && (m_cellWrapper.LeftExtent.BuiltInTypeKind == BuiltInTypeKind.EntitySet))
  160. {
  161. var associationSetMaps =
  162. ViewgenContext.EntityContainerMapping.GetRelationshipSetMappingsFor(m_cellWrapper.LeftExtent, cellQuery.Extent);
  163. var foreignKeySlots = new List<SlotInfo>();
  164. foreach (var colocatedAssociationSetMap in associationSetMaps)
  165. {
  166. WithRelationship withRelationship;
  167. if (TryGetWithRelationship(
  168. colocatedAssociationSetMap, m_cellWrapper.LeftExtent, cellQuery.SourceExtentMemberPath, ref foreignKeySlots,
  169. out withRelationship))
  170. {
  171. withRelationships.Add(withRelationship);
  172. totalProjectedSlots = projectedSlots.Concat(foreignKeySlots);
  173. }
  174. }
  175. }
  176. var result = new ExtentCqlBlock(
  177. cellQuery.Extent, cellQuery.SelectDistinctFlag, totalProjectedSlots.ToArray(),
  178. cellQuery.WhereClause, identifiers, ++blockAliasNum);
  179. return result;
  180. }
  181. private static bool TryGetWithRelationship(
  182. StorageAssociationSetMapping colocatedAssociationSetMap,
  183. EntitySetBase thisExtent,
  184. MemberPath sRootNode,
  185. ref List<SlotInfo> foreignKeySlots,
  186. out WithRelationship withRelationship)
  187. {
  188. DebugCheck.NotNull(foreignKeySlots);
  189. withRelationship = null;
  190. //Get the map for foreign key end
  191. var foreignKeyEndMap = GetForeignKeyEndMapFromAssocitionMap(colocatedAssociationSetMap);
  192. if (foreignKeyEndMap == null
  193. || foreignKeyEndMap.EndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
  194. {
  195. return false;
  196. }
  197. var toEnd = (AssociationEndMember)foreignKeyEndMap.EndMember;
  198. var fromEnd = MetadataHelper.GetOtherAssociationEnd(toEnd);
  199. var toEndEntityType = (EntityType)((RefType)(toEnd.TypeUsage.EdmType)).ElementType;
  200. var fromEndEntityType = (EntityType)(((RefType)fromEnd.TypeUsage.EdmType).ElementType);
  201. // Get the member path for AssociationSet
  202. var associationSet = (AssociationSet)colocatedAssociationSetMap.Set;
  203. var prefix = new MemberPath(associationSet, toEnd);
  204. // Collect the member paths for edm scalar properties that belong to the target entity key.
  205. // These will be used as part of WITH RELATIONSHIP.
  206. // Get the key properties from edm type since the query parser depends on the order of key members
  207. var propertyMaps = foreignKeyEndMap.Properties.Cast<StorageScalarPropertyMapping>();
  208. var toEndEntityKeyMemberPaths = new List<MemberPath>();
  209. foreach (EdmProperty edmProperty in toEndEntityType.KeyMembers)
  210. {
  211. var scalarPropertyMaps = propertyMaps.Where(propMap => (propMap.EdmProperty.Equals(edmProperty)));
  212. Debug.Assert(scalarPropertyMaps.Count() == 1, "Can't Map the same column multiple times in the same end");
  213. var scalarPropertyMap = scalarPropertyMaps.First();
  214. // Create SlotInfo for Freign Key member that needs to be projected.
  215. var sSlot = new MemberProjectedSlot(new MemberPath(sRootNode, scalarPropertyMap.ColumnProperty));
  216. var endMemberKeyPath = new MemberPath(prefix, edmProperty);
  217. toEndEntityKeyMemberPaths.Add(endMemberKeyPath);
  218. foreignKeySlots.Add(new SlotInfo(true, true, sSlot, endMemberKeyPath));
  219. }
  220. // Parent assignable from child: Ensures they are in the same hierarchy.
  221. if (thisExtent.ElementType.IsAssignableFrom(fromEndEntityType))
  222. {
  223. // Now create the WITH RELATIONSHIP with all the needed info.
  224. withRelationship = new WithRelationship(
  225. associationSet, fromEnd, fromEndEntityType, toEnd, toEndEntityType, toEndEntityKeyMemberPaths);
  226. return true;
  227. }
  228. else
  229. {
  230. return false;
  231. }
  232. }
  233. //Gets the end that is not mapped to the primary key of the table
  234. private static StorageEndPropertyMapping GetForeignKeyEndMapFromAssocitionMap(
  235. StorageAssociationSetMapping colocatedAssociationSetMap)
  236. {
  237. var mapFragment = colocatedAssociationSetMap.TypeMappings.First().MappingFragments.First();
  238. var storeEntitySet = (colocatedAssociationSetMap.StoreEntitySet);
  239. IEnumerable<EdmMember> keyProperties = storeEntitySet.ElementType.KeyMembers;
  240. //Find the end that's mapped to primary key
  241. foreach (StorageEndPropertyMapping endMap in mapFragment.Properties)
  242. {
  243. var endStoreMembers = endMap.StoreProperties;
  244. if (endStoreMembers.SequenceEqual(keyProperties, EqualityComparer<EdmMember>.Default))
  245. {
  246. //Return the map for the other end since that is the foreign key end
  247. var otherEnds = mapFragment.Properties.OfType<StorageEndPropertyMapping>().Where(eMap => (!eMap.Equals(endMap)));
  248. Debug.Assert(otherEnds.Count() == 1);
  249. return otherEnds.First();
  250. }
  251. }
  252. //This is probably defensive, but there should be no problem in falling back on the
  253. //AssociationSetMap if colocated foreign key is not found for some reason.
  254. return null;
  255. }
  256. // effects: See CellTreeNode.ToString
  257. internal override void ToCompactString(StringBuilder stringBuilder)
  258. {
  259. m_cellWrapper.ToCompactString(stringBuilder);
  260. }
  261. // A comparer that equates leaf nodes if the wrapper is the same
  262. private class LeafCellTreeNodeComparer : IEqualityComparer<LeafCellTreeNode>
  263. {
  264. public bool Equals(LeafCellTreeNode left, LeafCellTreeNode right)
  265. {
  266. // Quick check with references
  267. if (ReferenceEquals(left, right))
  268. {
  269. // Gets the Null and Undefined case as well
  270. return true;
  271. }
  272. // One of them is non-null at least
  273. if (left == null
  274. || right == null)
  275. {
  276. return false;
  277. }
  278. // Both are non-null at this point
  279. return left.m_cellWrapper.Equals(right.m_cellWrapper);
  280. }
  281. public int GetHashCode(LeafCellTreeNode node)
  282. {
  283. return node.m_cellWrapper.GetHashCode();
  284. }
  285. }
  286. }
  287. }