1 //--------------------------------------------------------------------- 2 // <copyright file="Propagator.PlaceholderVisitor.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // 6 // @owner Microsoft 7 // @backupOwner Microsoft 8 //--------------------------------------------------------------------- 9 10 namespace System.Data.Mapping.Update.Internal 11 { 12 using System.Collections.Generic; 13 using System.Data.Common; 14 using System.Data.Common.CommandTrees; 15 using System.Data.Metadata.Edm; 16 using System.Data.Spatial; 17 using System.Diagnostics; 18 19 internal partial class Propagator 20 { 21 /// <summary> 22 /// Class generating default records for extents. Has a single external entry point, the 23 /// <see cref="CreatePlaceholder" /> static method. 24 /// </summary> 25 private class ExtentPlaceholderCreator 26 { 27 #region Constructors 28 /// <summary> 29 /// Constructs a new placeholder creator. 30 /// </summary> 31 /// <param name="parent">Context used to generate all elements of the placeholder.</param> ExtentPlaceholderCreator(UpdateTranslator parent)32 private ExtentPlaceholderCreator(UpdateTranslator parent) 33 { 34 EntityUtil.CheckArgumentNull(parent, "parent"); 35 m_parent = parent; 36 } 37 #endregion 38 39 #region Fields 40 static private Dictionary<PrimitiveTypeKind, object> s_typeDefaultMap = InitializeTypeDefaultMap(); 41 private UpdateTranslator m_parent; 42 #endregion 43 44 #region Methods 45 /// <summary> 46 /// Initializes a map from primitive scalar types in the C-Space to default values 47 /// used within the placeholder. 48 /// </summary> InitializeTypeDefaultMap()49 private static Dictionary<PrimitiveTypeKind, object> InitializeTypeDefaultMap() 50 { 51 Dictionary<PrimitiveTypeKind, object> typeDefaultMap = new Dictionary<PrimitiveTypeKind, object>( 52 EqualityComparer<PrimitiveTypeKind>.Default); 53 54 // Use CLR defaults for value types, arbitrary constants for reference types 55 // (since these default to null) 56 typeDefaultMap[PrimitiveTypeKind.Binary] = new Byte[0]; 57 typeDefaultMap[PrimitiveTypeKind.Boolean] = default(Boolean); 58 typeDefaultMap[PrimitiveTypeKind.Byte] = default(Byte); 59 typeDefaultMap[PrimitiveTypeKind.DateTime] = default(DateTime); 60 typeDefaultMap[PrimitiveTypeKind.Time] = default(TimeSpan); 61 typeDefaultMap[PrimitiveTypeKind.DateTimeOffset] = default(DateTimeOffset); 62 typeDefaultMap[PrimitiveTypeKind.Decimal] = default(Decimal); 63 typeDefaultMap[PrimitiveTypeKind.Double] = default(Double); 64 typeDefaultMap[PrimitiveTypeKind.Guid] = default(Guid); 65 typeDefaultMap[PrimitiveTypeKind.Int16] = default(Int16); 66 typeDefaultMap[PrimitiveTypeKind.Int32] = default(Int32); 67 typeDefaultMap[PrimitiveTypeKind.Int64] = default(Int64); 68 typeDefaultMap[PrimitiveTypeKind.Single] = default(Single); 69 typeDefaultMap[PrimitiveTypeKind.SByte] = default(SByte); 70 typeDefaultMap[PrimitiveTypeKind.String] = String.Empty; 71 72 typeDefaultMap[PrimitiveTypeKind.Geometry] = DbGeometry.FromText("POINT EMPTY"); 73 typeDefaultMap[PrimitiveTypeKind.GeometryPoint] = DbGeometry.FromText("POINT EMPTY"); 74 typeDefaultMap[PrimitiveTypeKind.GeometryLineString] = DbGeometry.FromText("LINESTRING EMPTY"); 75 typeDefaultMap[PrimitiveTypeKind.GeometryPolygon] = DbGeometry.FromText("POLYGON EMPTY"); 76 typeDefaultMap[PrimitiveTypeKind.GeometryMultiPoint] = DbGeometry.FromText("MULTIPOINT EMPTY"); 77 typeDefaultMap[PrimitiveTypeKind.GeometryMultiLineString] = DbGeometry.FromText("MULTILINESTRING EMPTY"); 78 typeDefaultMap[PrimitiveTypeKind.GeometryMultiPolygon] = DbGeometry.FromText("MULTIPOLYGON EMPTY"); 79 typeDefaultMap[PrimitiveTypeKind.GeometryCollection] = DbGeometry.FromText("GEOMETRYCOLLECTION EMPTY"); 80 81 typeDefaultMap[PrimitiveTypeKind.Geography] = DbGeography.FromText("POINT EMPTY"); 82 typeDefaultMap[PrimitiveTypeKind.GeographyPoint] = DbGeography.FromText("POINT EMPTY"); 83 typeDefaultMap[PrimitiveTypeKind.GeographyLineString] = DbGeography.FromText("LINESTRING EMPTY"); 84 typeDefaultMap[PrimitiveTypeKind.GeographyPolygon] = DbGeography.FromText("POLYGON EMPTY"); 85 typeDefaultMap[PrimitiveTypeKind.GeographyMultiPoint] = DbGeography.FromText("MULTIPOINT EMPTY"); 86 typeDefaultMap[PrimitiveTypeKind.GeographyMultiLineString] = DbGeography.FromText("MULTILINESTRING EMPTY"); 87 typeDefaultMap[PrimitiveTypeKind.GeographyMultiPolygon] = DbGeography.FromText("MULTIPOLYGON EMPTY"); 88 typeDefaultMap[PrimitiveTypeKind.GeographyCollection] = DbGeography.FromText("GEOMETRYCOLLECTION EMPTY"); 89 90 #if DEBUG 91 foreach (object o in typeDefaultMap.Values) 92 { 93 Debug.Assert(null != o, "DbConstantExpression instances do not support null values"); 94 } 95 #endif 96 97 return typeDefaultMap; 98 } 99 100 /// <summary> 101 /// Creates a record for an extent containing default values. Assumes the extent is either 102 /// a relationship set or an entity set. 103 /// </summary> 104 /// <remarks> 105 /// Each scalar value appearing in the record is a <see cref="DbConstantExpression" />. A placeholder is created by recursively 106 /// building a record, so an entity record type will return a new record (<see cref="DbNewInstanceExpression" />) 107 /// consisting of some recursively built record for each column in the type. 108 /// </remarks> 109 /// <param name="extent">Extent</param> 110 /// <param name="parent">Command tree used to generate portions of the record</param> 111 /// <returns>A default record for the </returns> CreatePlaceholder(EntitySetBase extent, UpdateTranslator parent)112 internal static PropagatorResult CreatePlaceholder(EntitySetBase extent, UpdateTranslator parent) 113 { 114 EntityUtil.CheckArgumentNull(extent, "extent"); 115 116 ExtentPlaceholderCreator creator = new ExtentPlaceholderCreator(parent); 117 118 AssociationSet associationSet = extent as AssociationSet; 119 if (null != associationSet) 120 { 121 return creator.CreateAssociationSetPlaceholder(associationSet); 122 } 123 124 EntitySet entitySet = extent as EntitySet; 125 if (null != entitySet) 126 { 127 return creator.CreateEntitySetPlaceholder(entitySet); 128 } 129 130 throw EntityUtil.NotSupported(System.Data.Entity.Strings.Update_UnsupportedExtentType( 131 extent.Name, extent.GetType().Name)); 132 } 133 134 /// <summary> 135 /// Specialization of <see cref="CreatePlaceholder" /> for an entity set extent. 136 /// </summary> 137 /// <param name="entitySet"></param> 138 /// <returns></returns> CreateEntitySetPlaceholder(EntitySet entitySet)139 private PropagatorResult CreateEntitySetPlaceholder(EntitySet entitySet) 140 { 141 EntityUtil.CheckArgumentNull(entitySet, "entitySet"); 142 ReadOnlyMetadataCollection<EdmProperty> members = entitySet.ElementType.Properties; 143 PropagatorResult[] memberValues = new PropagatorResult[members.Count]; 144 145 for (int ordinal = 0; ordinal < members.Count; ordinal++) 146 { 147 PropagatorResult memberValue = CreateMemberPlaceholder(members[ordinal]); 148 memberValues[ordinal] = memberValue; 149 } 150 151 PropagatorResult result = PropagatorResult.CreateStructuralValue(memberValues, entitySet.ElementType, false); 152 153 return result; 154 } 155 156 /// <summary> 157 /// Specialization of <see cref="CreatePlaceholder" /> for a relationship set extent. 158 /// </summary> 159 /// <param name="associationSet"></param> 160 /// <returns></returns> CreateAssociationSetPlaceholder(AssociationSet associationSet)161 private PropagatorResult CreateAssociationSetPlaceholder(AssociationSet associationSet) 162 { 163 Debug.Assert(null != associationSet, "Caller must verify parameters are not null"); 164 165 var endMetadata = associationSet.ElementType.AssociationEndMembers; 166 PropagatorResult[] endReferenceValues = new PropagatorResult[endMetadata.Count]; 167 168 // Create a reference expression for each end in the relationship 169 for (int endOrdinal = 0; endOrdinal < endMetadata.Count; endOrdinal++) 170 { 171 var end = endMetadata[endOrdinal]; 172 EntityType entityType = (EntityType)((RefType)end.TypeUsage.EdmType).ElementType; 173 174 // Retrieve key values for this end 175 PropagatorResult[] keyValues = new PropagatorResult[entityType.KeyMembers.Count]; 176 for (int memberOrdinal = 0; memberOrdinal < entityType.KeyMembers.Count; memberOrdinal++) 177 { 178 EdmMember keyMember = entityType.KeyMembers[memberOrdinal]; 179 PropagatorResult keyValue = CreateMemberPlaceholder(keyMember); 180 keyValues[memberOrdinal] = keyValue; 181 } 182 183 RowType endType = entityType.GetKeyRowType(m_parent.MetadataWorkspace); 184 PropagatorResult refKeys = PropagatorResult.CreateStructuralValue(keyValues, endType, false); 185 186 endReferenceValues[endOrdinal] = refKeys; 187 } 188 189 PropagatorResult result = PropagatorResult.CreateStructuralValue(endReferenceValues, associationSet.ElementType, false); 190 return result; 191 } 192 193 /// <summary> 194 /// Returns a placeholder for a specific metadata member. 195 /// </summary> 196 /// <param name="member">EdmMember for which to produce a placeholder.</param> 197 /// <returns>Placeholder element for the given member.</returns> CreateMemberPlaceholder(EdmMember member)198 private PropagatorResult CreateMemberPlaceholder(EdmMember member) 199 { 200 EntityUtil.CheckArgumentNull(member, "member"); 201 202 return Visit(member); 203 } 204 205 #region Visitor implementation 206 /// <summary> 207 /// Given default values for children members, produces a new default expression for the requested (parent) member. 208 /// </summary> 209 /// <param name="node">Parent member</param> 210 /// <returns>Default value for parent member</returns> Visit(EdmMember node)211 internal PropagatorResult Visit(EdmMember node) 212 { 213 PropagatorResult result; 214 TypeUsage nodeType = Helper.GetModelTypeUsage(node); 215 216 if (Helper.IsScalarType(nodeType.EdmType)) 217 { 218 GetPropagatorResultForPrimitiveType(Helper.AsPrimitive(nodeType.EdmType), out result); 219 } 220 else 221 { 222 // Construct a new 'complex type' (really any structural type) member. 223 StructuralType structuralType = (StructuralType)nodeType.EdmType; 224 IBaseList<EdmMember> members = TypeHelpers.GetAllStructuralMembers(structuralType); 225 226 PropagatorResult[] args = new PropagatorResult[members.Count]; 227 for (int ordinal = 0; ordinal < members.Count; ordinal++) 228 // foreach (EdmMember member in members) 229 { 230 args[ordinal] = Visit(members[ordinal]); 231 } 232 233 result = PropagatorResult.CreateStructuralValue(args, structuralType, false); 234 } 235 236 return result; 237 } 238 239 // Find "sanctioned" default value GetPropagatorResultForPrimitiveType(PrimitiveType primitiveType, out PropagatorResult result)240 private static void GetPropagatorResultForPrimitiveType(PrimitiveType primitiveType, out PropagatorResult result) 241 { 242 object value; 243 PrimitiveTypeKind primitiveTypeKind = primitiveType.PrimitiveTypeKind; 244 if (!s_typeDefaultMap.TryGetValue(primitiveTypeKind, out value)) 245 { 246 // If none exists, default to lowest common denominator for constants 247 value = default(byte); 248 } 249 250 // Return a new constant expression flagged as unknown since the value is only there for 251 // show. (Not entirely for show, because null constraints may require a value for a record, 252 // whether that record is a placeholder or not). 253 result = PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, value); 254 } 255 256 #endregion 257 #endregion 258 } 259 } 260 } 261