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