Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataEntity / System / Data / Query / PlanCompiler / StructuredTypeInfo.cs / 1 / StructuredTypeInfo.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....], [....] //--------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; //using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class... using System.Globalization; using System.Linq; using System.Data.Common; using md = System.Data.Metadata.Edm; using System.Data.Query.InternalTrees; namespace System.Data.Query.PlanCompiler { ////// The type flattener module is part of the structured type elimination phase, /// and is largely responsible for "flattening" record and nominal types into /// flat record types. Additionally, for nominal types, this module produces typeid /// values that can be used later to interpret the input data stream. /// /// The goal of this module is to load up information about type and entityset metadata /// used in the ITree. This module is part of the "StructuredTypeElimination" phase, /// and provides information to help in this process. /// /// This module itself is broken down into multiple parts. /// /// (*) Loading type information: We walk the query tree to identify all references /// to structured types and entity sets /// /// (*) Processing entitysets: We walk the list of entitysets, and assign ids to each /// entityset. We also create a map of id->entityset metadata in this phase. /// /// (*) Processing types: We then walk the list of types, and process each type. This, /// in turn, is also broken into multiple parts: /// /// * Populating the Type Map: we walk the list of reference types and add each of /// them to our typeMap, along with their base types. /// /// * TypeId assignment: We assign typeids to each nominal (complextype/entitytype). /// This typeid is based on a dewey encoding. The typeid of a type is typically /// the typeid of its supertype suffixed by the subtype number of this type within /// its supertype. This encoding is intended to support easy type matching /// later on in the query - both for exact (IS OF ONLY) and inexact (IS OF) matches. /// /// * Type flattening: We then "explode"/"flatten" each structured type - refs, /// entity types, complex types and record types. The result is a flattened type /// where every single property of the resulting type is a primitive/scalar type /// (Note: UDTs are considered to be scalar types). Additional information may also /// be encoded as a type property. For example, a typeid property is added (if /// necessary) to complex/entity types to help discriminate polymorphic instances. /// An EntitySetId property is added to ref and entity type attributes to help /// determine the entity set that a given entity instance comes from. /// As part of type flattening, we keep track of additional information that allows /// us to map easily from the original property to the properties in the new type /// /// The final result of this processing is an object that contains: /// /// * a TypeInfo (extra type information) for each structured type in the query /// * a map from typeid value to type. To be used later by result assembly /// * a map between entitysetid value and entityset. To be used later by result assembly /// /// NOTE: StructuredTypeInfo is probably not the best name for this class, since /// it doesn't derive from TypeInfo but rather manages a collection of them. /// I don't have a better name, but if you come up with one change this. /// /// internal class StructuredTypeInfo { #region private state private md.TypeUsage m_stringType; private md.TypeUsage m_intType; private Dictionarym_typeInfoMap; private bool m_typeInfoMapPopulated; private md.EntitySet[] m_entitySetIdToEntitySetMap; //used as a Dictionary with the index as key private Dictionary m_entitySetToEntitySetIdMap; // A mapping from entity types to the "single" entityset (in the query) that can // produce instances of that entity. If there are multiple entitysets of the // same type, or "free-floating" entity constructors in the query, then // the corresponding entry is null private Dictionary m_entityTypeToEntitySetMap; private Dictionary m_discriminatorMaps; private RelPropertyHelper m_relPropertyHelper; private HashSet m_typesNeedingNullSentinel; #endregion #region constructor private StructuredTypeInfo(HashSet typesNeedingNullSentinel) { // Bug 428351: Make the type->typeInfo dictionary use ref equality for // types. The problem is that records (and other transient types) can // compare equal, even if they are not reference-equal, and this causes // us trouble down the road when we try to compare properties. // Type unification is a good thing, but it needs to happen earlier somewhere m_typeInfoMap = new Dictionary (TypeUsageEqualityComparer.Instance); m_typeInfoMapPopulated = false; m_typesNeedingNullSentinel = typesNeedingNullSentinel; } #endregion #region Process driver /// /// Process Driver /// /// /// list of structured types referenced in the query /// list of entitysets referenced in the query /// list of entity types that have "free-floating" entity constructors /// information on optimized discriminator patterns for entity sets /// helper for rel properties /// which types need a null sentinel /// internal static void Process(Command itree, ListreferencedTypes, List referencedEntitySets, List freeFloatingEntityConstructorTypes, Dictionary discriminatorMaps, RelPropertyHelper relPropertyHelper, HashSet typesNeedingNullSentinel, out StructuredTypeInfo structuredTypeInfo) { structuredTypeInfo = new StructuredTypeInfo(typesNeedingNullSentinel); structuredTypeInfo.Process(itree, referencedTypes, referencedEntitySets, freeFloatingEntityConstructorTypes, discriminatorMaps, relPropertyHelper); } /// /// Fills the StructuredTypeInfo instance from the itree provided. /// /// /// list of referenced structured types /// list of referenced entitysets /// list of free-floating entityConstructor types /// discriminator information for entity sets mapped using TPH pattern /// helper for rel properties private void Process(Command itree, ListreferencedTypes, List referencedEntitySets, List freeFloatingEntityConstructorTypes, Dictionary discriminatorMaps, RelPropertyHelper relPropertyHelper) { PlanCompiler.Assert(null != itree, "null itree?"); m_stringType = itree.StringType; m_intType = itree.IntegerType; m_relPropertyHelper = relPropertyHelper; ProcessEntitySets(referencedEntitySets, freeFloatingEntityConstructorTypes); ProcessDiscriminatorMaps(discriminatorMaps); ProcessTypes(referencedTypes); } #endregion #region "public" properties /// /// Mapping from entitysetid-s to entitysets /// internal md.EntitySet[] EntitySetIdToEntitySetMap { get { return m_entitySetIdToEntitySetMap; } } #endregion #region "public" methods ////// Get a helper for rel properties /// internal RelPropertyHelper RelPropertyHelper { get { return m_relPropertyHelper; } } ////// Gets the "single" entityset that stores instances of this type /// /// ///internal md.EntitySet GetEntitySet(md.EntityTypeBase type) { md.EntitySet set; md.EntityTypeBase rootType = GetRootType(type); if (!m_entityTypeToEntitySetMap.TryGetValue(rootType, out set)) { return null; } return set; } /// /// Get the entitysetid value for a given entityset /// /// the entityset ///entitysetid value internal int GetEntitySetId(md.EntitySet e) { int result = 0; if (!m_entitySetToEntitySetIdMap.TryGetValue(e, out result)) { PlanCompiler.Assert(false, "no such entity set?"); } return result; } ////// Gets entity sets referenced by the query. /// ///entity sets internal Common.Utils.SetGetEntitySets() { return new Common.Utils.Set (m_entitySetIdToEntitySetMap).MakeReadOnly(); } /// /// Find the TypeInfo entry for a type. For non-structured types, we always /// return null. For structured types, we return the entry in the typeInfoMap. /// If we don't find one, and the typeInfoMap has already been populated, then we /// assert /// /// the type to look up ///the typeinfo for the type (null if we couldn't find one) internal TypeInfo GetTypeInfo(md.TypeUsage type) { if (!TypeUtils.IsStructuredType(type)) { return null; } TypeInfo typeInfo = null; if (!m_typeInfoMap.TryGetValue(type, out typeInfo)) { PlanCompiler.Assert(!TypeUtils.IsStructuredType(type) || !m_typeInfoMapPopulated, "cannot find typeInfo for type " + type); } return typeInfo; } #endregion #region private methods #region EntitySet processing methods ////// Add a new entry to the entityTypeToSet map /// /// entity type /// entityset producing this type private void AddEntityTypeToSetEntry(md.EntityType entityType, md.EntitySet entitySet) { md.EntitySet other; md.EntityTypeBase rootType = GetRootType(entityType); bool hasSingleEntitySet = true; if (entitySet == null) { hasSingleEntitySet = false; } else if (m_entityTypeToEntitySetMap.TryGetValue(rootType, out other)) { if (other != entitySet) { hasSingleEntitySet = false; } } if (hasSingleEntitySet) { m_entityTypeToEntitySetMap[rootType] = entitySet; } else { m_entityTypeToEntitySetMap[rootType] = null; } } ////// Handle any relevant processing for entity sets /// list of referenced entitysets /// list of free-floating entity constructor types /// private void ProcessEntitySets(ListreferencedEntitySets, List freeFloatingEntityConstructorTypes) { AssignEntitySetIds(referencedEntitySets); // // set up the entity-type to set map // m_entityTypeToEntitySetMap = new Dictionary (); foreach (md.EntitySet e in referencedEntitySets) { AddEntityTypeToSetEntry(e.ElementType, e); } foreach (md.EntityType t in freeFloatingEntityConstructorTypes) { AddEntityTypeToSetEntry(t, null); } } /// /// Handle discriminator maps (determine which can safely be used in the query) /// private void ProcessDiscriminatorMaps(DictionarydiscriminatorMaps) { // Only use custom type discrimination where a type has a single entity set. Where // there are multiple sets, discriminator properties and flattened representations // may be incompatible. Dictionary filteredMaps = null; if (null != discriminatorMaps) { filteredMaps = new Dictionary (discriminatorMaps.Count, discriminatorMaps.Comparer); foreach (KeyValuePair setMapPair in discriminatorMaps) { md.EntitySetBase set = setMapPair.Key; ExplicitDiscriminatorMap map = setMapPair.Value.DiscriminatorMap; if (null != map) { md.EntityTypeBase rootType = GetRootType(set.ElementType); bool hasOneSet = GetEntitySet(rootType) != null; if (hasOneSet) { filteredMaps.Add(set, map); } } } if (filteredMaps.Count == 0) { // don't bother keeping the dictionary if it's empty filteredMaps = null; } } m_discriminatorMaps = filteredMaps; } /// /// Assign ids to each entityset in the query /// list of referenced entitysets /// private void AssignEntitySetIds(ListreferencedEntitySets) { m_entitySetIdToEntitySetMap = new md.EntitySet[referencedEntitySets.Count]; m_entitySetToEntitySetIdMap = new Dictionary (); int id = 0; foreach (md.EntitySet e in referencedEntitySets) { if (m_entitySetToEntitySetIdMap.ContainsKey(e)) { continue; } m_entitySetIdToEntitySetMap[id] = e; m_entitySetToEntitySetIdMap[e] = id; id++; } } #endregion #region Type processing methods /// /// Process all types in the query /// private void ProcessTypes(ListreferencedTypes) { // Build up auxilliary information for each type PopulateTypeInfoMap(referencedTypes); // Assign typeids to all nominal types AssignTypeIds(); // Then "explode" all types ExplodeTypes(); } #region Populating TypeInfo Map /// /// Build up auxilliary information for each referenced type in the query /// /// private void PopulateTypeInfoMap(ListreferencedTypes) { foreach (md.TypeUsage t in referencedTypes) { CreateTypeInfoForType(t); } m_typeInfoMapPopulated = true; } /// /// Tries to lookup custom discriminator map for the given type (applies to EntitySets with /// TPH discrimination pattern) /// private bool TryGetDiscriminatorMap(md.EdmType type, out ExplicitDiscriminatorMap discriminatorMap) { discriminatorMap = null; // check that there are actually discriminator maps available if (null == m_discriminatorMaps) { return false; } // must be an entity type... if (type.BuiltInTypeKind != md.BuiltInTypeKind.EntityType) { return false; } // get root entity type (discriminator maps are mapped from the root) md.EntityTypeBase rootEntityType = GetRootType((md.EntityType)type); // find entity set md.EntitySet entitySet; if (!m_entityTypeToEntitySetMap.TryGetValue(rootEntityType, out entitySet)) { return false; } // free floating entity constructors are stored with a null EntitySet if (entitySet == null) { return false; } // look for discriminator map return m_discriminatorMaps.TryGetValue(entitySet, out discriminatorMap); } ////// Create a TypeInfo (if necessary) for the type, and add it to the TypeInfo map /// /// the type to process private void CreateTypeInfoForType(md.TypeUsage type) { // // peel off all collection wrappers // while (TypeUtils.IsCollectionType(type)) { type = TypeHelpers.GetEdmType(type).TypeUsage; } // Only add "structured" types if (TypeUtils.IsStructuredType(type)) { // check for discriminator map... ExplicitDiscriminatorMap discriminatorMap; TryGetDiscriminatorMap(type.EdmType, out discriminatorMap); CreateTypeInfoForStructuredType(type, discriminatorMap); } } /// /// Add a new entry to the map. If an entry already exists, then this function /// simply returns the existing entry. Otherwise a new entry is created. If /// the type has a supertype, then we ensure that the supertype also exists in /// the map, and we add our info to the supertype's list of subtypes /// /// New type to add /// type discriminator map ///The TypeInfo for this type private TypeInfo CreateTypeInfoForStructuredType(md.TypeUsage type, ExplicitDiscriminatorMap discriminatorMap) { TypeInfo typeInfo; PlanCompiler.Assert(TypeUtils.IsStructuredType(type), "expected structured type. Found " + type); // Return existing entry, if one is available typeInfo = GetTypeInfo(type); if (typeInfo != null) { return typeInfo; } // Ensure that my supertype has been added to the map. TypeInfo superTypeInfo = null; md.RefType refType; if (type.EdmType.BaseType != null) { superTypeInfo = CreateTypeInfoForStructuredType(md.TypeUsage.Create(type.EdmType.BaseType), discriminatorMap); } // // Handle Ref types also in a similar fashion // else if (TypeHelpers.TryGetEdmType(type, out refType)) { md.EntityType entityType = refType.ElementType as md.EntityType; if (entityType != null && entityType.BaseType != null) { md.TypeUsage baseRefType = TypeHelpers.CreateReferenceTypeUsage(entityType.BaseType as md.EntityType); superTypeInfo = CreateTypeInfoForStructuredType(baseRefType, discriminatorMap); } } // // Add the types of my properties to the TypeInfo map // foreach (md.EdmMember m in TypeHelpers.GetDeclaredStructuralMembers(type)) { CreateTypeInfoForType(m.TypeUsage); } // // Get the types of the rel properties also // { md.EntityTypeBase entityType; if (TypeHelpers.TryGetEdmType (type, out entityType)) { foreach (RelProperty p in m_relPropertyHelper.GetDeclaredOnlyRelProperties(entityType)) { CreateTypeInfoForType(p.ToEnd.TypeUsage); } } } // Now add myself to the map typeInfo = TypeInfo.Create(type, superTypeInfo, discriminatorMap); m_typeInfoMap.Add(type, typeInfo); return typeInfo; } #endregion #region Assigning TypeIds /// /// Assigns typeids to each type in the map. /// We walk the map looking only for "root" types, and call the function /// above to process root types. All other types will be handled in that /// function /// private void AssignTypeIds() { int typeNum = 0; foreach (KeyValuePairkv in m_typeInfoMap) { // See if there is a declared discriminator value for this column if (kv.Value.RootType.DiscriminatorMap != null) { // find discriminator value for type var entityType = (md.EntityType)kv.Key.EdmType; kv.Value.TypeId = kv.Value.RootType.DiscriminatorMap.GetTypeId(entityType); } // Only handle root types. The call below will ensure that all the // subtypes are appropriately tagged else if (kv.Value.IsRootType && (md.TypeSemantics.IsEntityType(kv.Key) || md.TypeSemantics.IsComplexType(kv.Key))) { AssignRootTypeId(kv.Value, String.Format(CultureInfo.InvariantCulture, "{0}X", typeNum)); typeNum++; } } } /// /// Assign a typeid to a root type /// /// /// private void AssignRootTypeId(TypeInfo typeInfo, string typeId) { typeInfo.TypeId = typeId; AssignTypeIdsToSubTypes(typeInfo); } ////// Assigns typeids to each subtype of the current type. /// Assertion: the current type has already had a typeid assigned to it. /// /// The current type private void AssignTypeIdsToSubTypes(TypeInfo typeInfo) { // Now walk through all my subtypes, and assign their typeids int mySubTypeNum = 0; foreach (TypeInfo subtype in typeInfo.ImmediateSubTypes) { AssignTypeId(subtype, mySubTypeNum); mySubTypeNum++; } } ////// Assign a typeid to a non-root type. /// Assigns typeids to a non-root type based on a dewey encoding scheme. /// The typeid will be the typeId of the supertype suffixed by a /// local identifier for the type. /// /// the non-root type /// position in the subtype list private void AssignTypeId(TypeInfo typeInfo, int subtypeNum) { typeInfo.TypeId = String.Format(CultureInfo.InvariantCulture, "{0}{1}X", typeInfo.SuperType.TypeId, subtypeNum); AssignTypeIdsToSubTypes(typeInfo); } #endregion #region Flattening/Exploding types ////// A type needs a type-id property if it is an entity type or a complex tpe that /// has subtypes. /// Coming soon: relax the "need subtype" requirement (ie) any entity/complex type will /// have a typeid /// /// ///private bool NeedsTypeIdProperty(TypeInfo typeInfo) { return typeInfo.ImmediateSubTypes.Count > 0 && !md.TypeSemantics.IsReferenceType(typeInfo.Type); } /// /// A type needs a null-sentinel property if it is an row type that was projected /// at the top level of the query; we capture that information in the preprocessor /// and pass it in here. /// /// ///private bool NeedsNullSentinelProperty(TypeInfo typeInfo) { return m_typesNeedingNullSentinel.Contains(typeInfo.Type.EdmType.Identity); } /// /// The type needs an entitysetidproperty, if it is either an entity type /// or a reference type, AND we cannot determine that there is only entityset /// in the query that could be producing instances of this entity /// /// ///private bool NeedsEntitySetIdProperty(TypeInfo typeInfo) { md.EntityType entityType; md.RefType refType = typeInfo.Type.EdmType as md.RefType; if (refType != null) { entityType = refType.ElementType as md.EntityType; } else { entityType = typeInfo.Type.EdmType as md.EntityType; } bool result = ((entityType != null) && (GetEntitySet(entityType) == null)); return result; } /// /// "Explode" each type in the dictionary. (ie) for each type, get a flattened /// list of all its members (including special cases for the typeid) /// private void ExplodeTypes() { // Walk through the list of types, and only process the supertypes, since // The ExplodeType method will ensure that all the subtypes are appropriately // tagged foreach (KeyValuePairkv in m_typeInfoMap) { if (kv.Value.IsRootType) { ExplodeType(kv.Value); } } } /// /// "Explode" a type. (ie) produce a flat record type with one property for each /// scalar property (top-level or nested) of the original type. /// Really deals with structured types, but also /// peels off collection wrappers /// /// the type to explode ///the typeinfo for this type (with the explosion) private TypeInfo ExplodeType(md.TypeUsage type) { if (TypeUtils.IsStructuredType(type)) { TypeInfo typeInfo = GetTypeInfo(type); ExplodeType(typeInfo); return typeInfo; } if (TypeUtils.IsCollectionType(type)) { md.TypeUsage elementType = TypeHelpers.GetEdmType(type).TypeUsage; ExplodeType(elementType); return null; } return null; } /// /// Type Explosion - simply delegates to the root type /// /// type info private void ExplodeType(TypeInfo typeInfo) { ExplodeRootStructuredType(typeInfo.RootType); } ////// "Explode" a root type. (ie) add each member of the type to a flat list of /// members for the supertype. /// /// Type explosion works in a DFS style model. We first walk through the /// list of properties for the current type, and "flatten" out the properties /// that are themselves "structured". We then target each subtype (recursively) /// and perform the same kind of processing. /// /// Consider a very simple case: /// /// Q = (z1 int, z2 date) /// Q2: Q = (z3 string) -- Q2 is a subtype of Q /// T = (a int, b Q, c date) /// S: T = (d int) -- read as S is a subtype of T /// /// The result of flattening T (and S) will be /// /// (a int, b.z1 int, b.z2 date, b.z3 string, c date, d int) /// /// the root type to explode private void ExplodeRootStructuredType(RootTypeInfo rootType) { // Already done?? if (rootType.FlattenedType != null) { return; } // // Special handling for root types. Add any special // properties that are needed - TypeId, EntitySetId, etc // if (NeedsTypeIdProperty(rootType)) { rootType.AddPropertyRef(TypeIdPropertyRef.Instance); // check for discriminator map; if one exists, use custom discriminator member; otherwise, use default if (null != rootType.DiscriminatorMap) { rootType.TypeIdKind = TypeIdKind.UserSpecified; rootType.TypeIdType = md.Helper.GetModelTypeUsage(rootType.DiscriminatorMap.DiscriminatorProperty); } else { rootType.TypeIdKind = TypeIdKind.Generated; rootType.TypeIdType = m_stringType; } } if (NeedsEntitySetIdProperty(rootType)) { rootType.AddPropertyRef(EntitySetIdPropertyRef.Instance); } if (NeedsNullSentinelProperty(rootType)) { rootType.AddPropertyRef(NullSentinelPropertyRef.Instance); } // // Then add members from each type in the hierarchy (including // the root type) // ExplodeRootStructuredTypeHelper(rootType); // // For entity types, add all the rel-properties now. Note that rel-properties // are added after the regular properties of all subtypes // if (md.TypeSemantics.IsEntityType(rootType.Type)) { AddRelProperties(rootType); } // // We've now gotten all the relevant properties // Now let's create a new record type // CreateFlattenedRecordType(rootType); } ////// Helper for ExplodeType. /// Walks through each member introduced by the current type, and /// adds it onto the "flat" record type being constructed. /// We then walk through all subtypes of this type, and process those as /// well. /// Special handling for Refs: we only add the keys; there is no /// need to handle subtypes (since they won't be introducing anything /// different) /// /// type in the type hierarchy private void ExplodeRootStructuredTypeHelper(TypeInfo typeInfo) { RootTypeInfo rootType = typeInfo.RootType; // Identify the members of this type. For Refs, use the key properties // of the target entity type. For all other types, simply use the type // members IEnumerable typeMembers = null; md.RefType refType; if (TypeHelpers.TryGetEdmType(typeInfo.Type, out refType)) { // // If this is not the root type, then don't bother adding the keys. // the root type has already done this // if (!typeInfo.IsRootType) { return; } typeMembers = refType.ElementType.KeyMembers; } else { typeMembers = TypeHelpers.GetDeclaredStructuralMembers(typeInfo.Type); } // Walk through all the members of the type foreach (md.EdmMember p in typeMembers) { TypeInfo propertyType = ExplodeType(p.TypeUsage); // // If we can't find a TypeInfo for this property's type, then it must // be a scalar type or a collection type. In either case, we'll // build up a SimplePropertyRef // if (propertyType == null) { rootType.AddPropertyRef(new SimplePropertyRef(p)); } else { // // We're dealing with a structured type again. Create NestedPropertyRef // for each property of the nested type // foreach (PropertyRef nestedPropInfo in propertyType.PropertyRefList) { rootType.AddPropertyRef(nestedPropInfo.CreateNestedPropertyRef(p)); } } } // // Process all subtypes now // foreach (TypeInfo subTypeInfo in typeInfo.ImmediateSubTypes) { ExplodeRootStructuredTypeHelper(subTypeInfo); } } /// /// Add the list of rel-properties for this type /// /// the type to process private void AddRelProperties(TypeInfo typeInfo) { md.EntityTypeBase entityType = (md.EntityTypeBase)typeInfo.Type.EdmType; // // Walk through each rel-property defined for this specific type, // and add a corresponding property-ref // foreach (RelProperty p in m_relPropertyHelper.GetDeclaredOnlyRelProperties(entityType)) { md.EdmType refType = p.ToEnd.TypeUsage.EdmType; TypeInfo refTypeInfo = GetTypeInfo(p.ToEnd.TypeUsage); // // We're dealing with a structured type again - flatten this out // as well // ExplodeType(refTypeInfo); foreach (PropertyRef nestedPropInfo in refTypeInfo.PropertyRefList) { typeInfo.RootType.AddPropertyRef(nestedPropInfo.CreateNestedPropertyRef(p)); } } // // Process all subtypes now // foreach (TypeInfo subTypeInfo in typeInfo.ImmediateSubTypes) { AddRelProperties(subTypeInfo); } } ////// Create the flattened record type for the type. /// Walk through the list of property refs, and creates a new field /// (which we name as "F1", "F2" etc.) with the required property type. /// /// We then produce a mapping from the original property (propertyRef really) /// to the new property for use in later modules. /// /// Finally, we identify the TypeId and EntitySetId property if they exist /// /// private void CreateFlattenedRecordType(RootTypeInfo type) { // // If this type corresponds to an entity type, and that entity type // has no subtypes, and that that entity type has no complex properties // then simply use the name from that property // bool usePropertyNamesFromUnderlyingType; if (md.TypeSemantics.IsEntityType(type.Type) && type.ImmediateSubTypes.Count == 0) { usePropertyNamesFromUnderlyingType = true; } else { usePropertyNamesFromUnderlyingType = false; } // Build the record type List> fieldList = new List >(); int fieldId = 0; foreach (PropertyRef p in type.PropertyRefList) { string fieldName = null; if (usePropertyNamesFromUnderlyingType) { SimplePropertyRef simpleP = p as SimplePropertyRef; if (simpleP != null) { fieldName = simpleP.Property.Name; } } md.TypeUsage propertyType = GetPropertyType(type, p); if (fieldName == null) { // // Deal with collisions? // fieldName = "F" + fieldId.ToString(CultureInfo.InvariantCulture); } fieldList.Add(new KeyValuePair (fieldName, propertyType)); fieldId++; } type.FlattenedType = TypeHelpers.CreateRowType(fieldList); // Now build up the property map IEnumerator origProps = type.PropertyRefList.GetEnumerator(); foreach (md.EdmProperty p in type.FlattenedType.Properties) { if (!origProps.MoveNext()) { PlanCompiler.Assert(false, "property refs count and flattened type member count mismatch?"); } type.AddPropertyMapping(origProps.Current, p); } } /// /// Get the "new" type corresponding to the input type. For structured types, /// we return the flattened record type. /// For collections of structured type, we return a new collection type of the corresponding flattened /// type. /// For everything else, we return the input type /// /// the original type ///the new type (if any) private md.TypeUsage GetNewType(md.TypeUsage type) { if (TypeUtils.IsStructuredType(type)) { TypeInfo typeInfo = GetTypeInfo(type); return typeInfo.FlattenedTypeUsage; } md.TypeUsage elementType; if (TypeHelpers.TryGetCollectionElementType(type, out elementType)) { md.TypeUsage newElementType = GetNewType(elementType); if (newElementType.EdmEquals(elementType)) { return type; } else { return TypeHelpers.CreateCollectionTypeUsage(newElementType); } } // simple scalar return type; } ////// Get the datatype for a propertyRef. The only concrete classes that we /// handle are TypeIdPropertyRef, and BasicPropertyRef. /// AllPropertyRef is illegal here. /// For BasicPropertyRef, we simply pick up the type from the corresponding /// property. For TypeIdPropertyRef, we use "string" as the default type /// or the discriminator property type where one is available. /// /// typeinfo of the current type /// current property ref ///the datatype of the property private md.TypeUsage GetPropertyType(RootTypeInfo typeInfo, PropertyRef p) { md.TypeUsage result = null; PropertyRef innerProperty = null; // Get the "leaf" property first while (p is NestedPropertyRef) { NestedPropertyRef npr = (NestedPropertyRef)p; p = npr.OuterProperty; innerProperty = npr.InnerProperty; } if (p is TypeIdPropertyRef) { // // Get to the innermost type that specifies this typeid (the entity type), // get the datatype for the typeid column from that type // if (innerProperty != null && innerProperty is SimplePropertyRef) { md.TypeUsage innerType = ((SimplePropertyRef)innerProperty).Property.TypeUsage; TypeInfo innerTypeInfo = GetTypeInfo(innerType); result = innerTypeInfo.RootType.TypeIdType; } else { result = typeInfo.TypeIdType; } } else if (p is EntitySetIdPropertyRef || p is NullSentinelPropertyRef) { result = m_intType; } else if (p is RelPropertyRef) { result = (p as RelPropertyRef).Property.ToEnd.TypeUsage; } else { SimplePropertyRef simpleP = p as SimplePropertyRef; if (simpleP != null) { result = md.Helper.GetModelTypeUsage(simpleP.Property); } } result = GetNewType(result); PlanCompiler.Assert(null != result, "unrecognized property type?"); return result; } #endregion #endregion #region utils ////// Get the root entity type for a type /// /// entity type ///private static md.EntityTypeBase GetRootType(md.EntityTypeBase type) { while (type.BaseType != null) { type = (md.EntityTypeBase)type.BaseType; } return type; } #endregion #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //---------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....], [....] //--------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; //using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class... using System.Globalization; using System.Linq; using System.Data.Common; using md = System.Data.Metadata.Edm; using System.Data.Query.InternalTrees; namespace System.Data.Query.PlanCompiler { ////// The type flattener module is part of the structured type elimination phase, /// and is largely responsible for "flattening" record and nominal types into /// flat record types. Additionally, for nominal types, this module produces typeid /// values that can be used later to interpret the input data stream. /// /// The goal of this module is to load up information about type and entityset metadata /// used in the ITree. This module is part of the "StructuredTypeElimination" phase, /// and provides information to help in this process. /// /// This module itself is broken down into multiple parts. /// /// (*) Loading type information: We walk the query tree to identify all references /// to structured types and entity sets /// /// (*) Processing entitysets: We walk the list of entitysets, and assign ids to each /// entityset. We also create a map of id->entityset metadata in this phase. /// /// (*) Processing types: We then walk the list of types, and process each type. This, /// in turn, is also broken into multiple parts: /// /// * Populating the Type Map: we walk the list of reference types and add each of /// them to our typeMap, along with their base types. /// /// * TypeId assignment: We assign typeids to each nominal (complextype/entitytype). /// This typeid is based on a dewey encoding. The typeid of a type is typically /// the typeid of its supertype suffixed by the subtype number of this type within /// its supertype. This encoding is intended to support easy type matching /// later on in the query - both for exact (IS OF ONLY) and inexact (IS OF) matches. /// /// * Type flattening: We then "explode"/"flatten" each structured type - refs, /// entity types, complex types and record types. The result is a flattened type /// where every single property of the resulting type is a primitive/scalar type /// (Note: UDTs are considered to be scalar types). Additional information may also /// be encoded as a type property. For example, a typeid property is added (if /// necessary) to complex/entity types to help discriminate polymorphic instances. /// An EntitySetId property is added to ref and entity type attributes to help /// determine the entity set that a given entity instance comes from. /// As part of type flattening, we keep track of additional information that allows /// us to map easily from the original property to the properties in the new type /// /// The final result of this processing is an object that contains: /// /// * a TypeInfo (extra type information) for each structured type in the query /// * a map from typeid value to type. To be used later by result assembly /// * a map between entitysetid value and entityset. To be used later by result assembly /// /// NOTE: StructuredTypeInfo is probably not the best name for this class, since /// it doesn't derive from TypeInfo but rather manages a collection of them. /// I don't have a better name, but if you come up with one change this. /// /// internal class StructuredTypeInfo { #region private state private md.TypeUsage m_stringType; private md.TypeUsage m_intType; private Dictionarym_typeInfoMap; private bool m_typeInfoMapPopulated; private md.EntitySet[] m_entitySetIdToEntitySetMap; //used as a Dictionary with the index as key private Dictionary m_entitySetToEntitySetIdMap; // A mapping from entity types to the "single" entityset (in the query) that can // produce instances of that entity. If there are multiple entitysets of the // same type, or "free-floating" entity constructors in the query, then // the corresponding entry is null private Dictionary m_entityTypeToEntitySetMap; private Dictionary m_discriminatorMaps; private RelPropertyHelper m_relPropertyHelper; private HashSet m_typesNeedingNullSentinel; #endregion #region constructor private StructuredTypeInfo(HashSet typesNeedingNullSentinel) { // Bug 428351: Make the type->typeInfo dictionary use ref equality for // types. The problem is that records (and other transient types) can // compare equal, even if they are not reference-equal, and this causes // us trouble down the road when we try to compare properties. // Type unification is a good thing, but it needs to happen earlier somewhere m_typeInfoMap = new Dictionary (TypeUsageEqualityComparer.Instance); m_typeInfoMapPopulated = false; m_typesNeedingNullSentinel = typesNeedingNullSentinel; } #endregion #region Process driver /// /// Process Driver /// /// /// list of structured types referenced in the query /// list of entitysets referenced in the query /// list of entity types that have "free-floating" entity constructors /// information on optimized discriminator patterns for entity sets /// helper for rel properties /// which types need a null sentinel /// internal static void Process(Command itree, ListreferencedTypes, List referencedEntitySets, List freeFloatingEntityConstructorTypes, Dictionary discriminatorMaps, RelPropertyHelper relPropertyHelper, HashSet typesNeedingNullSentinel, out StructuredTypeInfo structuredTypeInfo) { structuredTypeInfo = new StructuredTypeInfo(typesNeedingNullSentinel); structuredTypeInfo.Process(itree, referencedTypes, referencedEntitySets, freeFloatingEntityConstructorTypes, discriminatorMaps, relPropertyHelper); } /// /// Fills the StructuredTypeInfo instance from the itree provided. /// /// /// list of referenced structured types /// list of referenced entitysets /// list of free-floating entityConstructor types /// discriminator information for entity sets mapped using TPH pattern /// helper for rel properties private void Process(Command itree, ListreferencedTypes, List referencedEntitySets, List freeFloatingEntityConstructorTypes, Dictionary discriminatorMaps, RelPropertyHelper relPropertyHelper) { PlanCompiler.Assert(null != itree, "null itree?"); m_stringType = itree.StringType; m_intType = itree.IntegerType; m_relPropertyHelper = relPropertyHelper; ProcessEntitySets(referencedEntitySets, freeFloatingEntityConstructorTypes); ProcessDiscriminatorMaps(discriminatorMaps); ProcessTypes(referencedTypes); } #endregion #region "public" properties /// /// Mapping from entitysetid-s to entitysets /// internal md.EntitySet[] EntitySetIdToEntitySetMap { get { return m_entitySetIdToEntitySetMap; } } #endregion #region "public" methods ////// Get a helper for rel properties /// internal RelPropertyHelper RelPropertyHelper { get { return m_relPropertyHelper; } } ////// Gets the "single" entityset that stores instances of this type /// /// ///internal md.EntitySet GetEntitySet(md.EntityTypeBase type) { md.EntitySet set; md.EntityTypeBase rootType = GetRootType(type); if (!m_entityTypeToEntitySetMap.TryGetValue(rootType, out set)) { return null; } return set; } /// /// Get the entitysetid value for a given entityset /// /// the entityset ///entitysetid value internal int GetEntitySetId(md.EntitySet e) { int result = 0; if (!m_entitySetToEntitySetIdMap.TryGetValue(e, out result)) { PlanCompiler.Assert(false, "no such entity set?"); } return result; } ////// Gets entity sets referenced by the query. /// ///entity sets internal Common.Utils.SetGetEntitySets() { return new Common.Utils.Set (m_entitySetIdToEntitySetMap).MakeReadOnly(); } /// /// Find the TypeInfo entry for a type. For non-structured types, we always /// return null. For structured types, we return the entry in the typeInfoMap. /// If we don't find one, and the typeInfoMap has already been populated, then we /// assert /// /// the type to look up ///the typeinfo for the type (null if we couldn't find one) internal TypeInfo GetTypeInfo(md.TypeUsage type) { if (!TypeUtils.IsStructuredType(type)) { return null; } TypeInfo typeInfo = null; if (!m_typeInfoMap.TryGetValue(type, out typeInfo)) { PlanCompiler.Assert(!TypeUtils.IsStructuredType(type) || !m_typeInfoMapPopulated, "cannot find typeInfo for type " + type); } return typeInfo; } #endregion #region private methods #region EntitySet processing methods ////// Add a new entry to the entityTypeToSet map /// /// entity type /// entityset producing this type private void AddEntityTypeToSetEntry(md.EntityType entityType, md.EntitySet entitySet) { md.EntitySet other; md.EntityTypeBase rootType = GetRootType(entityType); bool hasSingleEntitySet = true; if (entitySet == null) { hasSingleEntitySet = false; } else if (m_entityTypeToEntitySetMap.TryGetValue(rootType, out other)) { if (other != entitySet) { hasSingleEntitySet = false; } } if (hasSingleEntitySet) { m_entityTypeToEntitySetMap[rootType] = entitySet; } else { m_entityTypeToEntitySetMap[rootType] = null; } } ////// Handle any relevant processing for entity sets /// list of referenced entitysets /// list of free-floating entity constructor types /// private void ProcessEntitySets(ListreferencedEntitySets, List freeFloatingEntityConstructorTypes) { AssignEntitySetIds(referencedEntitySets); // // set up the entity-type to set map // m_entityTypeToEntitySetMap = new Dictionary (); foreach (md.EntitySet e in referencedEntitySets) { AddEntityTypeToSetEntry(e.ElementType, e); } foreach (md.EntityType t in freeFloatingEntityConstructorTypes) { AddEntityTypeToSetEntry(t, null); } } /// /// Handle discriminator maps (determine which can safely be used in the query) /// private void ProcessDiscriminatorMaps(DictionarydiscriminatorMaps) { // Only use custom type discrimination where a type has a single entity set. Where // there are multiple sets, discriminator properties and flattened representations // may be incompatible. Dictionary filteredMaps = null; if (null != discriminatorMaps) { filteredMaps = new Dictionary (discriminatorMaps.Count, discriminatorMaps.Comparer); foreach (KeyValuePair setMapPair in discriminatorMaps) { md.EntitySetBase set = setMapPair.Key; ExplicitDiscriminatorMap map = setMapPair.Value.DiscriminatorMap; if (null != map) { md.EntityTypeBase rootType = GetRootType(set.ElementType); bool hasOneSet = GetEntitySet(rootType) != null; if (hasOneSet) { filteredMaps.Add(set, map); } } } if (filteredMaps.Count == 0) { // don't bother keeping the dictionary if it's empty filteredMaps = null; } } m_discriminatorMaps = filteredMaps; } /// /// Assign ids to each entityset in the query /// list of referenced entitysets /// private void AssignEntitySetIds(ListreferencedEntitySets) { m_entitySetIdToEntitySetMap = new md.EntitySet[referencedEntitySets.Count]; m_entitySetToEntitySetIdMap = new Dictionary (); int id = 0; foreach (md.EntitySet e in referencedEntitySets) { if (m_entitySetToEntitySetIdMap.ContainsKey(e)) { continue; } m_entitySetIdToEntitySetMap[id] = e; m_entitySetToEntitySetIdMap[e] = id; id++; } } #endregion #region Type processing methods /// /// Process all types in the query /// private void ProcessTypes(ListreferencedTypes) { // Build up auxilliary information for each type PopulateTypeInfoMap(referencedTypes); // Assign typeids to all nominal types AssignTypeIds(); // Then "explode" all types ExplodeTypes(); } #region Populating TypeInfo Map /// /// Build up auxilliary information for each referenced type in the query /// /// private void PopulateTypeInfoMap(ListreferencedTypes) { foreach (md.TypeUsage t in referencedTypes) { CreateTypeInfoForType(t); } m_typeInfoMapPopulated = true; } /// /// Tries to lookup custom discriminator map for the given type (applies to EntitySets with /// TPH discrimination pattern) /// private bool TryGetDiscriminatorMap(md.EdmType type, out ExplicitDiscriminatorMap discriminatorMap) { discriminatorMap = null; // check that there are actually discriminator maps available if (null == m_discriminatorMaps) { return false; } // must be an entity type... if (type.BuiltInTypeKind != md.BuiltInTypeKind.EntityType) { return false; } // get root entity type (discriminator maps are mapped from the root) md.EntityTypeBase rootEntityType = GetRootType((md.EntityType)type); // find entity set md.EntitySet entitySet; if (!m_entityTypeToEntitySetMap.TryGetValue(rootEntityType, out entitySet)) { return false; } // free floating entity constructors are stored with a null EntitySet if (entitySet == null) { return false; } // look for discriminator map return m_discriminatorMaps.TryGetValue(entitySet, out discriminatorMap); } ////// Create a TypeInfo (if necessary) for the type, and add it to the TypeInfo map /// /// the type to process private void CreateTypeInfoForType(md.TypeUsage type) { // // peel off all collection wrappers // while (TypeUtils.IsCollectionType(type)) { type = TypeHelpers.GetEdmType(type).TypeUsage; } // Only add "structured" types if (TypeUtils.IsStructuredType(type)) { // check for discriminator map... ExplicitDiscriminatorMap discriminatorMap; TryGetDiscriminatorMap(type.EdmType, out discriminatorMap); CreateTypeInfoForStructuredType(type, discriminatorMap); } } /// /// Add a new entry to the map. If an entry already exists, then this function /// simply returns the existing entry. Otherwise a new entry is created. If /// the type has a supertype, then we ensure that the supertype also exists in /// the map, and we add our info to the supertype's list of subtypes /// /// New type to add /// type discriminator map ///The TypeInfo for this type private TypeInfo CreateTypeInfoForStructuredType(md.TypeUsage type, ExplicitDiscriminatorMap discriminatorMap) { TypeInfo typeInfo; PlanCompiler.Assert(TypeUtils.IsStructuredType(type), "expected structured type. Found " + type); // Return existing entry, if one is available typeInfo = GetTypeInfo(type); if (typeInfo != null) { return typeInfo; } // Ensure that my supertype has been added to the map. TypeInfo superTypeInfo = null; md.RefType refType; if (type.EdmType.BaseType != null) { superTypeInfo = CreateTypeInfoForStructuredType(md.TypeUsage.Create(type.EdmType.BaseType), discriminatorMap); } // // Handle Ref types also in a similar fashion // else if (TypeHelpers.TryGetEdmType(type, out refType)) { md.EntityType entityType = refType.ElementType as md.EntityType; if (entityType != null && entityType.BaseType != null) { md.TypeUsage baseRefType = TypeHelpers.CreateReferenceTypeUsage(entityType.BaseType as md.EntityType); superTypeInfo = CreateTypeInfoForStructuredType(baseRefType, discriminatorMap); } } // // Add the types of my properties to the TypeInfo map // foreach (md.EdmMember m in TypeHelpers.GetDeclaredStructuralMembers(type)) { CreateTypeInfoForType(m.TypeUsage); } // // Get the types of the rel properties also // { md.EntityTypeBase entityType; if (TypeHelpers.TryGetEdmType (type, out entityType)) { foreach (RelProperty p in m_relPropertyHelper.GetDeclaredOnlyRelProperties(entityType)) { CreateTypeInfoForType(p.ToEnd.TypeUsage); } } } // Now add myself to the map typeInfo = TypeInfo.Create(type, superTypeInfo, discriminatorMap); m_typeInfoMap.Add(type, typeInfo); return typeInfo; } #endregion #region Assigning TypeIds /// /// Assigns typeids to each type in the map. /// We walk the map looking only for "root" types, and call the function /// above to process root types. All other types will be handled in that /// function /// private void AssignTypeIds() { int typeNum = 0; foreach (KeyValuePairkv in m_typeInfoMap) { // See if there is a declared discriminator value for this column if (kv.Value.RootType.DiscriminatorMap != null) { // find discriminator value for type var entityType = (md.EntityType)kv.Key.EdmType; kv.Value.TypeId = kv.Value.RootType.DiscriminatorMap.GetTypeId(entityType); } // Only handle root types. The call below will ensure that all the // subtypes are appropriately tagged else if (kv.Value.IsRootType && (md.TypeSemantics.IsEntityType(kv.Key) || md.TypeSemantics.IsComplexType(kv.Key))) { AssignRootTypeId(kv.Value, String.Format(CultureInfo.InvariantCulture, "{0}X", typeNum)); typeNum++; } } } /// /// Assign a typeid to a root type /// /// /// private void AssignRootTypeId(TypeInfo typeInfo, string typeId) { typeInfo.TypeId = typeId; AssignTypeIdsToSubTypes(typeInfo); } ////// Assigns typeids to each subtype of the current type. /// Assertion: the current type has already had a typeid assigned to it. /// /// The current type private void AssignTypeIdsToSubTypes(TypeInfo typeInfo) { // Now walk through all my subtypes, and assign their typeids int mySubTypeNum = 0; foreach (TypeInfo subtype in typeInfo.ImmediateSubTypes) { AssignTypeId(subtype, mySubTypeNum); mySubTypeNum++; } } ////// Assign a typeid to a non-root type. /// Assigns typeids to a non-root type based on a dewey encoding scheme. /// The typeid will be the typeId of the supertype suffixed by a /// local identifier for the type. /// /// the non-root type /// position in the subtype list private void AssignTypeId(TypeInfo typeInfo, int subtypeNum) { typeInfo.TypeId = String.Format(CultureInfo.InvariantCulture, "{0}{1}X", typeInfo.SuperType.TypeId, subtypeNum); AssignTypeIdsToSubTypes(typeInfo); } #endregion #region Flattening/Exploding types ////// A type needs a type-id property if it is an entity type or a complex tpe that /// has subtypes. /// Coming soon: relax the "need subtype" requirement (ie) any entity/complex type will /// have a typeid /// /// ///private bool NeedsTypeIdProperty(TypeInfo typeInfo) { return typeInfo.ImmediateSubTypes.Count > 0 && !md.TypeSemantics.IsReferenceType(typeInfo.Type); } /// /// A type needs a null-sentinel property if it is an row type that was projected /// at the top level of the query; we capture that information in the preprocessor /// and pass it in here. /// /// ///private bool NeedsNullSentinelProperty(TypeInfo typeInfo) { return m_typesNeedingNullSentinel.Contains(typeInfo.Type.EdmType.Identity); } /// /// The type needs an entitysetidproperty, if it is either an entity type /// or a reference type, AND we cannot determine that there is only entityset /// in the query that could be producing instances of this entity /// /// ///private bool NeedsEntitySetIdProperty(TypeInfo typeInfo) { md.EntityType entityType; md.RefType refType = typeInfo.Type.EdmType as md.RefType; if (refType != null) { entityType = refType.ElementType as md.EntityType; } else { entityType = typeInfo.Type.EdmType as md.EntityType; } bool result = ((entityType != null) && (GetEntitySet(entityType) == null)); return result; } /// /// "Explode" each type in the dictionary. (ie) for each type, get a flattened /// list of all its members (including special cases for the typeid) /// private void ExplodeTypes() { // Walk through the list of types, and only process the supertypes, since // The ExplodeType method will ensure that all the subtypes are appropriately // tagged foreach (KeyValuePairkv in m_typeInfoMap) { if (kv.Value.IsRootType) { ExplodeType(kv.Value); } } } /// /// "Explode" a type. (ie) produce a flat record type with one property for each /// scalar property (top-level or nested) of the original type. /// Really deals with structured types, but also /// peels off collection wrappers /// /// the type to explode ///the typeinfo for this type (with the explosion) private TypeInfo ExplodeType(md.TypeUsage type) { if (TypeUtils.IsStructuredType(type)) { TypeInfo typeInfo = GetTypeInfo(type); ExplodeType(typeInfo); return typeInfo; } if (TypeUtils.IsCollectionType(type)) { md.TypeUsage elementType = TypeHelpers.GetEdmType(type).TypeUsage; ExplodeType(elementType); return null; } return null; } /// /// Type Explosion - simply delegates to the root type /// /// type info private void ExplodeType(TypeInfo typeInfo) { ExplodeRootStructuredType(typeInfo.RootType); } ////// "Explode" a root type. (ie) add each member of the type to a flat list of /// members for the supertype. /// /// Type explosion works in a DFS style model. We first walk through the /// list of properties for the current type, and "flatten" out the properties /// that are themselves "structured". We then target each subtype (recursively) /// and perform the same kind of processing. /// /// Consider a very simple case: /// /// Q = (z1 int, z2 date) /// Q2: Q = (z3 string) -- Q2 is a subtype of Q /// T = (a int, b Q, c date) /// S: T = (d int) -- read as S is a subtype of T /// /// The result of flattening T (and S) will be /// /// (a int, b.z1 int, b.z2 date, b.z3 string, c date, d int) /// /// the root type to explode private void ExplodeRootStructuredType(RootTypeInfo rootType) { // Already done?? if (rootType.FlattenedType != null) { return; } // // Special handling for root types. Add any special // properties that are needed - TypeId, EntitySetId, etc // if (NeedsTypeIdProperty(rootType)) { rootType.AddPropertyRef(TypeIdPropertyRef.Instance); // check for discriminator map; if one exists, use custom discriminator member; otherwise, use default if (null != rootType.DiscriminatorMap) { rootType.TypeIdKind = TypeIdKind.UserSpecified; rootType.TypeIdType = md.Helper.GetModelTypeUsage(rootType.DiscriminatorMap.DiscriminatorProperty); } else { rootType.TypeIdKind = TypeIdKind.Generated; rootType.TypeIdType = m_stringType; } } if (NeedsEntitySetIdProperty(rootType)) { rootType.AddPropertyRef(EntitySetIdPropertyRef.Instance); } if (NeedsNullSentinelProperty(rootType)) { rootType.AddPropertyRef(NullSentinelPropertyRef.Instance); } // // Then add members from each type in the hierarchy (including // the root type) // ExplodeRootStructuredTypeHelper(rootType); // // For entity types, add all the rel-properties now. Note that rel-properties // are added after the regular properties of all subtypes // if (md.TypeSemantics.IsEntityType(rootType.Type)) { AddRelProperties(rootType); } // // We've now gotten all the relevant properties // Now let's create a new record type // CreateFlattenedRecordType(rootType); } ////// Helper for ExplodeType. /// Walks through each member introduced by the current type, and /// adds it onto the "flat" record type being constructed. /// We then walk through all subtypes of this type, and process those as /// well. /// Special handling for Refs: we only add the keys; there is no /// need to handle subtypes (since they won't be introducing anything /// different) /// /// type in the type hierarchy private void ExplodeRootStructuredTypeHelper(TypeInfo typeInfo) { RootTypeInfo rootType = typeInfo.RootType; // Identify the members of this type. For Refs, use the key properties // of the target entity type. For all other types, simply use the type // members IEnumerable typeMembers = null; md.RefType refType; if (TypeHelpers.TryGetEdmType(typeInfo.Type, out refType)) { // // If this is not the root type, then don't bother adding the keys. // the root type has already done this // if (!typeInfo.IsRootType) { return; } typeMembers = refType.ElementType.KeyMembers; } else { typeMembers = TypeHelpers.GetDeclaredStructuralMembers(typeInfo.Type); } // Walk through all the members of the type foreach (md.EdmMember p in typeMembers) { TypeInfo propertyType = ExplodeType(p.TypeUsage); // // If we can't find a TypeInfo for this property's type, then it must // be a scalar type or a collection type. In either case, we'll // build up a SimplePropertyRef // if (propertyType == null) { rootType.AddPropertyRef(new SimplePropertyRef(p)); } else { // // We're dealing with a structured type again. Create NestedPropertyRef // for each property of the nested type // foreach (PropertyRef nestedPropInfo in propertyType.PropertyRefList) { rootType.AddPropertyRef(nestedPropInfo.CreateNestedPropertyRef(p)); } } } // // Process all subtypes now // foreach (TypeInfo subTypeInfo in typeInfo.ImmediateSubTypes) { ExplodeRootStructuredTypeHelper(subTypeInfo); } } /// /// Add the list of rel-properties for this type /// /// the type to process private void AddRelProperties(TypeInfo typeInfo) { md.EntityTypeBase entityType = (md.EntityTypeBase)typeInfo.Type.EdmType; // // Walk through each rel-property defined for this specific type, // and add a corresponding property-ref // foreach (RelProperty p in m_relPropertyHelper.GetDeclaredOnlyRelProperties(entityType)) { md.EdmType refType = p.ToEnd.TypeUsage.EdmType; TypeInfo refTypeInfo = GetTypeInfo(p.ToEnd.TypeUsage); // // We're dealing with a structured type again - flatten this out // as well // ExplodeType(refTypeInfo); foreach (PropertyRef nestedPropInfo in refTypeInfo.PropertyRefList) { typeInfo.RootType.AddPropertyRef(nestedPropInfo.CreateNestedPropertyRef(p)); } } // // Process all subtypes now // foreach (TypeInfo subTypeInfo in typeInfo.ImmediateSubTypes) { AddRelProperties(subTypeInfo); } } ////// Create the flattened record type for the type. /// Walk through the list of property refs, and creates a new field /// (which we name as "F1", "F2" etc.) with the required property type. /// /// We then produce a mapping from the original property (propertyRef really) /// to the new property for use in later modules. /// /// Finally, we identify the TypeId and EntitySetId property if they exist /// /// private void CreateFlattenedRecordType(RootTypeInfo type) { // // If this type corresponds to an entity type, and that entity type // has no subtypes, and that that entity type has no complex properties // then simply use the name from that property // bool usePropertyNamesFromUnderlyingType; if (md.TypeSemantics.IsEntityType(type.Type) && type.ImmediateSubTypes.Count == 0) { usePropertyNamesFromUnderlyingType = true; } else { usePropertyNamesFromUnderlyingType = false; } // Build the record type List> fieldList = new List >(); int fieldId = 0; foreach (PropertyRef p in type.PropertyRefList) { string fieldName = null; if (usePropertyNamesFromUnderlyingType) { SimplePropertyRef simpleP = p as SimplePropertyRef; if (simpleP != null) { fieldName = simpleP.Property.Name; } } md.TypeUsage propertyType = GetPropertyType(type, p); if (fieldName == null) { // // Deal with collisions? // fieldName = "F" + fieldId.ToString(CultureInfo.InvariantCulture); } fieldList.Add(new KeyValuePair (fieldName, propertyType)); fieldId++; } type.FlattenedType = TypeHelpers.CreateRowType(fieldList); // Now build up the property map IEnumerator origProps = type.PropertyRefList.GetEnumerator(); foreach (md.EdmProperty p in type.FlattenedType.Properties) { if (!origProps.MoveNext()) { PlanCompiler.Assert(false, "property refs count and flattened type member count mismatch?"); } type.AddPropertyMapping(origProps.Current, p); } } /// /// Get the "new" type corresponding to the input type. For structured types, /// we return the flattened record type. /// For collections of structured type, we return a new collection type of the corresponding flattened /// type. /// For everything else, we return the input type /// /// the original type ///the new type (if any) private md.TypeUsage GetNewType(md.TypeUsage type) { if (TypeUtils.IsStructuredType(type)) { TypeInfo typeInfo = GetTypeInfo(type); return typeInfo.FlattenedTypeUsage; } md.TypeUsage elementType; if (TypeHelpers.TryGetCollectionElementType(type, out elementType)) { md.TypeUsage newElementType = GetNewType(elementType); if (newElementType.EdmEquals(elementType)) { return type; } else { return TypeHelpers.CreateCollectionTypeUsage(newElementType); } } // simple scalar return type; } ////// Get the datatype for a propertyRef. The only concrete classes that we /// handle are TypeIdPropertyRef, and BasicPropertyRef. /// AllPropertyRef is illegal here. /// For BasicPropertyRef, we simply pick up the type from the corresponding /// property. For TypeIdPropertyRef, we use "string" as the default type /// or the discriminator property type where one is available. /// /// typeinfo of the current type /// current property ref ///the datatype of the property private md.TypeUsage GetPropertyType(RootTypeInfo typeInfo, PropertyRef p) { md.TypeUsage result = null; PropertyRef innerProperty = null; // Get the "leaf" property first while (p is NestedPropertyRef) { NestedPropertyRef npr = (NestedPropertyRef)p; p = npr.OuterProperty; innerProperty = npr.InnerProperty; } if (p is TypeIdPropertyRef) { // // Get to the innermost type that specifies this typeid (the entity type), // get the datatype for the typeid column from that type // if (innerProperty != null && innerProperty is SimplePropertyRef) { md.TypeUsage innerType = ((SimplePropertyRef)innerProperty).Property.TypeUsage; TypeInfo innerTypeInfo = GetTypeInfo(innerType); result = innerTypeInfo.RootType.TypeIdType; } else { result = typeInfo.TypeIdType; } } else if (p is EntitySetIdPropertyRef || p is NullSentinelPropertyRef) { result = m_intType; } else if (p is RelPropertyRef) { result = (p as RelPropertyRef).Property.ToEnd.TypeUsage; } else { SimplePropertyRef simpleP = p as SimplePropertyRef; if (simpleP != null) { result = md.Helper.GetModelTypeUsage(simpleP.Property); } } result = GetNewType(result); PlanCompiler.Assert(null != result, "unrecognized property type?"); return result; } #endregion #endregion #region utils ////// Get the root entity type for a type /// /// entity type ///private static md.EntityTypeBase GetRootType(md.EntityTypeBase type) { while (type.BaseType != null) { type = (md.EntityTypeBase)type.BaseType; } return type; } #endregion #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- Flattener.cs
- CapabilitiesSection.cs
- TableAutomationPeer.cs
- XamlPathDataSerializer.cs
- DoubleLinkListEnumerator.cs
- FixedSOMPage.cs
- VarRemapper.cs
- PropertyCollection.cs
- ToolConsole.cs
- DataServiceConfiguration.cs
- AssemblyBuilderData.cs
- ByteStreamMessage.cs
- EdmSchemaError.cs
- httpapplicationstate.cs
- BitmapEffectrendercontext.cs
- BaseCodePageEncoding.cs
- SQLString.cs
- EventLogPermissionAttribute.cs
- EtwProvider.cs
- UserControl.cs
- Binding.cs
- Viewport3DVisual.cs
- QilGeneratorEnv.cs
- FunctionImportElement.cs
- Label.cs
- XamlBuildTaskServices.cs
- Utils.cs
- InvokeDelegate.cs
- TreeNodeCollection.cs
- Model3DGroup.cs
- DropShadowEffect.cs
- CultureMapper.cs
- CheckBoxBaseAdapter.cs
- RoleBoolean.cs
- DatatypeImplementation.cs
- Calendar.cs
- RtfToXamlLexer.cs
- CommandID.cs
- ReflectionPermission.cs
- RegistrationServices.cs
- PolyLineSegmentFigureLogic.cs
- StateMachineWorkflow.cs
- MiniModule.cs
- SelectQueryOperator.cs
- ReferenceEqualityComparer.cs
- OverlappedAsyncResult.cs
- TableAutomationPeer.cs
- System.Data_BID.cs
- KeyValuePairs.cs
- FixedBufferAttribute.cs
- AlignmentXValidation.cs
- DesignerSerializerAttribute.cs
- FrameworkName.cs
- LiteralControl.cs
- SqlConnectionHelper.cs
- XmlSchemaProviderAttribute.cs
- DataSourceConverter.cs
- XmlSchemaSubstitutionGroup.cs
- ControlCollection.cs
- ColumnClickEvent.cs
- Journal.cs
- OletxVolatileEnlistment.cs
- WebBrowserContainer.cs
- HiddenField.cs
- AppSettingsExpressionBuilder.cs
- HistoryEventArgs.cs
- SpellerError.cs
- PathGradientBrush.cs
- FileLogRecordHeader.cs
- Scheduling.cs
- GenericTypeParameterBuilder.cs
- X509ImageLogo.cs
- CacheChildrenQuery.cs
- NativeCompoundFileAPIs.cs
- FrameworkElement.cs
- UnicastIPAddressInformationCollection.cs
- TCPClient.cs
- IConvertible.cs
- Image.cs
- MetabaseServerConfig.cs
- SettingsAttributes.cs
- ConfigsHelper.cs
- StorageConditionPropertyMapping.cs
- QilSortKey.cs
- TypedTableBaseExtensions.cs
- OdbcFactory.cs
- DesignTimeParseData.cs
- XmlTextReaderImplHelpers.cs
- Action.cs
- ScaleTransform3D.cs
- SafeRegistryKey.cs
- ExtenderControl.cs
- MemoryRecordBuffer.cs
- SHA256.cs
- XmlKeywords.cs
- ResolvedKeyFrameEntry.cs
- Condition.cs
- DynamicRouteExpression.cs
- DetailsViewRow.cs
- DataGridSortingEventArgs.cs