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 Dictionary m_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,
List referencedTypes,
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,
List referencedTypes,
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.Set GetEntitySets() {
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(List referencedEntitySets, 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(Dictionary discriminatorMaps)
{
// 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(List referencedEntitySets) {
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(List referencedTypes) {
// 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(List referencedTypes) {
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 (KeyValuePair kv 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 (KeyValuePair kv 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 Dictionary m_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,
List referencedTypes,
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,
List referencedTypes,
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.Set GetEntitySets() {
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(List referencedEntitySets, 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(Dictionary discriminatorMaps)
{
// 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(List referencedEntitySets) {
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(List referencedTypes) {
// 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(List referencedTypes) {
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 (KeyValuePair kv 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 (KeyValuePair kv 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
- OracleTransaction.cs
- TypedReference.cs
- Literal.cs
- ContactManager.cs
- FileDataSourceCache.cs
- XPathChildIterator.cs
- TagPrefixInfo.cs
- ParseHttpDate.cs
- FileNotFoundException.cs
- SelectionPatternIdentifiers.cs
- RemoteWebConfigurationHostStream.cs
- EncryptedData.cs
- PointValueSerializer.cs
- EntityDataSourceMemberPath.cs
- ComEventsMethod.cs
- EventLogEntryCollection.cs
- InheritanceContextChangedEventManager.cs
- DataGridViewElement.cs
- ArrayList.cs
- XPathException.cs
- SafeFileMapViewHandle.cs
- Function.cs
- ServiceMemoryGates.cs
- ProcessModelInfo.cs
- SendMessageContent.cs
- OrderByBuilder.cs
- PassportAuthentication.cs
- HtmlLink.cs
- FixedSOMLineCollection.cs
- ActivityXamlServices.cs
- PerspectiveCamera.cs
- CodeDOMUtility.cs
- DivideByZeroException.cs
- TypeLibConverter.cs
- Base64Stream.cs
- ProfileService.cs
- XmlSerializerNamespaces.cs
- EventSourceCreationData.cs
- HttpCacheVaryByContentEncodings.cs
- FlowLayoutPanelDesigner.cs
- ProfileSection.cs
- COM2IPerPropertyBrowsingHandler.cs
- ReferentialConstraint.cs
- IdentitySection.cs
- BindingList.cs
- ChtmlPhoneCallAdapter.cs
- EntityWrapperFactory.cs
- ToolStripSeparatorRenderEventArgs.cs
- QueryOutputWriterV1.cs
- PersistenceMetadataNamespace.cs
- ButtonChrome.cs
- ColorPalette.cs
- FatalException.cs
- FixedSOMGroup.cs
- SettingsPropertyIsReadOnlyException.cs
- Vector3DAnimationBase.cs
- Activator.cs
- ContentElement.cs
- RenderTargetBitmap.cs
- ListBase.cs
- RepeatButtonAutomationPeer.cs
- MenuItemBinding.cs
- PassportIdentity.cs
- SecurityProtocol.cs
- TreeViewItem.cs
- BasicExpandProvider.cs
- ProfileService.cs
- ManagementNamedValueCollection.cs
- Single.cs
- Bold.cs
- UnsignedPublishLicense.cs
- CaseStatement.cs
- EmbeddedObject.cs
- SqlServices.cs
- ValidatorCompatibilityHelper.cs
- LicenseContext.cs
- PropertyPushdownHelper.cs
- PassportIdentity.cs
- SamlAttribute.cs
- MobileCapabilities.cs
- ProcessMonitor.cs
- PrePostDescendentsWalker.cs
- MetadataExchangeBindings.cs
- SqlGenerator.cs
- NetPipeSection.cs
- DeviceFilterEditorDialog.cs
- ServiceNotStartedException.cs
- ChangeNode.cs
- DataGridViewCheckBoxColumn.cs
- Comparer.cs
- WebPartDescription.cs
- WizardDesigner.cs
- ResourceCategoryAttribute.cs
- XmlUtil.cs
- DelegateTypeInfo.cs
- HtmlInputImage.cs
- LinkUtilities.cs
- FileDialog.cs
- Activity.cs
- RequestTimeoutManager.cs