Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Mapping / DefaultObjectMappingItemCollection.cs
1 //---------------------------------------------------------------------
2 // <copyright file="DefaultObjectMappingItemCollection.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5
6 // @owner       [....]
7 // @backupOwner [....]
8 //---------------------------------------------------------------------
9
10 namespace System.Data.Mapping {
11
12     using System;
13     using System.Collections;
14     using System.Collections.Generic;
15     using System.Data.Entity;
16     using System.Data.Metadata.Edm;
17     using System.Diagnostics;
18     using System.Globalization;
19     using System.Linq;
20
21     /// <summary>
22     /// The class creates a default OCMapping between a TypeMetadata in O space
23     /// and an TypeMetadata in Edm space. The loader expects that for each member in 
24     /// C space type there exists a member in O space type that has the same name. The member maps will be stored in
25     /// C space member order.    
26     /// </summary>
27     internal class DefaultObjectMappingItemCollection : MappingItemCollection {
28         #region Constructors
29         /// <summary>
30         /// Constrcutor to create an instance of DefaultObjectMappingItemCollection.
31         /// To start with we will create a Schema under which maps will be created.
32         /// </summary>
33         /// <param name="edmCollection"></param>
34         /// <param name="objectCollection"></param>
35         public DefaultObjectMappingItemCollection(EdmItemCollection edmCollection,
36             ObjectItemCollection objectCollection) : base(DataSpace.OCSpace)
37         {
38             EntityUtil.CheckArgumentNull(edmCollection, "edmCollection");
39             EntityUtil.CheckArgumentNull(objectCollection, "objectCollection");
40             this.m_edmCollection = edmCollection;
41             this.m_objectCollection = objectCollection;
42             LoadPrimitiveMaps();
43         }
44         #endregion
45
46         #region Fields
47         private ObjectItemCollection m_objectCollection;
48         private EdmItemCollection m_edmCollection;
49         private Dictionary<string, int> clrTypeIndexes = new Dictionary<string, int>(StringComparer.Ordinal); //Indexes into the type mappings collection based on clr type name
50         private Dictionary<string, int> cdmTypeIndexes = new Dictionary<string, int>(StringComparer.Ordinal); //Indexes into the type mappings collection based on clr type name
51         #endregion
52
53         #region Methods
54         /// <summary>
55         /// Search for a Mapping metadata with the specified type key.
56         /// </summary>
57         /// <param name="identity">identity of the type</param>
58         /// <param name="typeSpace">The dataspace that the type for which map needs to be returned belongs to</param>
59         /// <param name="ignoreCase">true for case-insensitive lookup</param>
60         /// <exception cref="ArgumentException"> Thrown if mapping space is not valid</exception>
61         internal override Map GetMap(string identity, DataSpace typeSpace, bool ignoreCase)
62         {
63             Map map;
64             if (!this.TryGetMap(identity, typeSpace, ignoreCase, out map))
65             {
66                 throw new InvalidOperationException(System.Data.Entity.Strings.Mapping_Object_InvalidType(identity));
67             }
68             return map;
69         }
70
71         /// <summary>
72         /// Search for a Mapping metadata with the specified type key.
73         /// </summary>
74         /// <param name="identity">identity of the type</param>
75         /// <param name="typeSpace">The dataspace that the type for which map needs to be returned belongs to</param>
76         /// <param name="ignoreCase">true for case-insensitive lookup</param>
77         /// <param name="map"></param>
78         /// <returns>Returns false if no match found.</returns>
79         internal override bool TryGetMap(string identity, DataSpace typeSpace, bool ignoreCase, out Map map)
80         {
81             EdmType cdmType = null;
82             EdmType clrType = null;
83             if (typeSpace == DataSpace.CSpace)
84             {
85                 if (ignoreCase)
86                 {
87                     // Get the correct casing of the identity first if we are asked to do ignore case
88                     if (!m_edmCollection.TryGetItem<EdmType>(identity, true, out cdmType))
89                     {
90                         map = null;
91                         return false;
92                     }
93
94                     identity = cdmType.Identity;
95                 }
96
97                 int index;
98                 if (cdmTypeIndexes.TryGetValue(identity, out index))
99                 {
100                     map = (Map)this[index];
101                     return true;
102                 }
103
104                 if (cdmType != null ||
105                     m_edmCollection.TryGetItem<EdmType>(identity, ignoreCase, out cdmType))
106                 {
107                     // If the mapping is not already loaded, then get the mapping ospace type
108                     m_objectCollection.TryGetOSpaceType(cdmType, out clrType);
109                 }
110             }
111             else if (typeSpace == DataSpace.OSpace)
112             {
113                 if (ignoreCase)
114                 {
115                     // Get the correct casing of the identity first if we are asked to do ignore case
116                     if (!m_objectCollection.TryGetItem<EdmType>(identity, true, out clrType))
117                     {
118                         map = null;
119                         return false;
120                     }
121
122                     identity = clrType.Identity;
123                 }
124
125                 int index;
126                 if (clrTypeIndexes.TryGetValue(identity, out index))
127                 {
128                     map = (Map)this[index];
129                     return true;
130                 }
131
132                 if (clrType != null ||
133                     m_objectCollection.TryGetItem<EdmType>(identity, ignoreCase, out clrType))
134                 {
135                     // If the mapping is not already loaded, get the mapping cspace type
136                     string cspaceTypeName = ObjectItemCollection.TryGetMappingCSpaceTypeIdentity(clrType);
137                     m_edmCollection.TryGetItem<EdmType>(cspaceTypeName, out cdmType);
138                 }
139             }
140
141             if ((clrType == null) || (cdmType == null))
142             {
143                 map = null;
144                 return false;
145             }
146             else
147             {
148                 map = this.GetDefaultMapping(cdmType, clrType);
149                 return true;
150             }
151         }
152
153         /// <summary>
154         /// Search for a Mapping metadata with the specified type key.
155         /// </summary>
156         /// <param name="identity">identity of the type</param>
157         /// <param name="typeSpace">The dataspace that the type for which map needs to be returned belongs to</param>
158         /// <exception cref="ArgumentException"> Thrown if mapping space is not valid</exception>
159         internal override Map GetMap(string identity, DataSpace typeSpace)
160         {
161             return this.GetMap(identity, typeSpace, false /*ignoreCase*/);
162         }
163
164         /// <summary>
165         /// Search for a Mapping metadata with the specified type key.
166         /// </summary>
167         /// <param name="identity">identity of the type</param>
168         /// <param name="typeSpace">The dataspace that the type for which map needs to be returned belongs to</param>
169         /// <param name="map"></param>
170         /// <returns>Returns false if no match found.</returns>
171         internal override bool TryGetMap(string identity, DataSpace typeSpace, out Map map)
172         {
173             return this.TryGetMap(identity, typeSpace, false /*ignoreCase*/, out map);
174         }
175
176         /// <summary>
177         /// Search for a Mapping metadata with the specified type key.
178         /// </summary>
179         /// <param name="item"></param>
180         internal override Map GetMap(GlobalItem item)
181         {
182             EntityUtil.CheckArgumentNull(item, "item");
183             Map map;
184             if (!this.TryGetMap(item, out map))
185             {
186                 throw new InvalidOperationException(System.Data.Entity.Strings.Mapping_Object_InvalidType(item.Identity));
187             }
188             return map;
189         }
190
191         /// <summary>
192         /// Search for a Mapping metadata with the specified type key.
193         /// </summary>
194         /// <param name="item"></param>
195         /// <param name="map"></param>
196         /// <returns>Returns false if no match found.</returns>
197         internal override bool TryGetMap(GlobalItem item, out Map map)
198         {
199             if (item == null)
200             {
201                 map = null;
202                 return false;
203             }
204
205             DataSpace typeSpace = item.DataSpace;
206
207             //For transient types just create a map on fly and return
208             EdmType edmType = item as EdmType;
209             if (edmType != null)
210             {
211                 if (Helper.IsTransientType(edmType))
212                 {
213                     map = GetOCMapForTransientType(edmType, typeSpace);
214                     if (map != null)
215                     {
216                         return true;
217                     }
218                     else
219                     {
220                         return false;
221                     }
222                 }
223             }
224             return this.TryGetMap(item.Identity, typeSpace, out map);
225         }
226
227         /// <summary>
228         /// The method creates a default mapping between two TypeMetadatas - one in 
229         /// C space and one in O space. The precondition for calling this method is that
230         /// the type in Object space contains the members with the same name as those of defined in
231         /// C space. It is not required the otherway.
232         /// </summary>
233         /// <param name="cdmType"></param>
234         /// <param name="clrType"></param>
235         private Map GetDefaultMapping(EdmType cdmType, EdmType clrType) {
236             Debug.Assert((cdmType != null) && (clrType != null));
237             return LoadObjectMapping(cdmType, clrType, this);
238         }
239
240         private Map GetOCMapForTransientType(EdmType edmType, DataSpace typeSpace)
241         {
242             Debug.Assert(typeSpace == DataSpace.CSpace || typeSpace == DataSpace.OSpace || Helper.IsRowType(edmType) || Helper.IsCollectionType(edmType));
243             EdmType clrType = null;
244             EdmType cdmType = null;
245             int index = -1;
246             if (typeSpace != DataSpace.OSpace)
247             {
248                 if (cdmTypeIndexes.TryGetValue(edmType.Identity, out index))
249                 {
250                     return (Map)this[index];
251                 }
252                 else
253                 {
254                     cdmType = edmType;
255                     clrType = ConvertCSpaceToOSpaceType(edmType);
256                 }
257             }
258             else if (typeSpace == DataSpace.OSpace)
259             {
260                 if (clrTypeIndexes.TryGetValue(edmType.Identity, out index))
261                 {
262                     return (Map)this[index];
263                 }
264                 else
265                 {
266                     clrType = edmType;
267                     cdmType = ConvertOSpaceToCSpaceType(clrType);
268                 }
269             }
270
271             ObjectTypeMapping typeMapping = new ObjectTypeMapping(clrType, cdmType);
272             if (BuiltInTypeKind.RowType == edmType.BuiltInTypeKind)
273             {
274                 RowType clrRowType = (RowType)clrType;
275                 RowType edmRowType = (RowType)cdmType;
276
277                 Debug.Assert(clrRowType.Properties.Count == edmRowType.Properties.Count, "Property count mismatch");
278                 for (int idx = 0; idx < clrRowType.Properties.Count; idx++)
279                 {
280                     typeMapping.AddMemberMap(new ObjectPropertyMapping(edmRowType.Properties[idx], clrRowType.Properties[idx]));
281                 }
282             }
283             if ( (!cdmTypeIndexes.ContainsKey(cdmType.Identity)) && (!clrTypeIndexes.ContainsKey(clrType.Identity)) )
284             {
285             AddInternalMapping(typeMapping);
286             }
287             return typeMapping;
288         }
289
290         /// <summary>Convert CSpace TypeMetadata into OSpace TypeMetadata</summary>
291         /// <param name="cdmType"></param>
292         /// <returns>OSpace type metadata</returns>
293         private EdmType ConvertCSpaceToOSpaceType(EdmType cdmType)
294         {
295             EdmType clrType = null;
296
297             if (Helper.IsCollectionType(cdmType))
298             {
299                 EdmType elemType = ConvertCSpaceToOSpaceType(((CollectionType)cdmType).TypeUsage.EdmType);
300                 clrType = new CollectionType(elemType);
301             }
302             else if (Helper.IsRowType(cdmType))
303             {
304                 List<EdmProperty> clrProperties = new List<EdmProperty>();
305                 foreach (EdmProperty column in ((RowType)cdmType).Properties)
306                 {
307                     EdmType clrPropertyType = ConvertCSpaceToOSpaceType(column.TypeUsage.EdmType);
308                     EdmProperty clrProperty = new EdmProperty(column.Name, TypeUsage.Create(clrPropertyType));
309                     clrProperties.Add(clrProperty);
310                 }
311                 clrType = new RowType(clrProperties, ((RowType)cdmType).InitializerMetadata);
312             }
313             else if (Helper.IsRefType(cdmType))
314             {
315                 clrType = new RefType((EntityType)ConvertCSpaceToOSpaceType(((RefType)cdmType).ElementType));
316             }
317             else if (Helper.IsPrimitiveType(cdmType))
318             {
319                 clrType = m_objectCollection.GetMappedPrimitiveType(((PrimitiveType)cdmType).PrimitiveTypeKind);
320             }
321             else
322             {
323                 clrType = ((ObjectTypeMapping)GetMap(cdmType)).ClrType;
324             }
325             Debug.Assert((null != clrType), "null converted clr type");
326             return clrType;
327         }
328
329         /// <summary>Convert CSpace TypeMetadata into OSpace TypeMetadata</summary>
330         /// <param name="clrType"></param>
331         /// <returns>OSpace type metadata</returns>
332         private EdmType ConvertOSpaceToCSpaceType(EdmType clrType)
333         {
334             EdmType cdmType = null;
335
336             if (Helper.IsCollectionType(clrType))
337             {
338                 EdmType elemType = ConvertOSpaceToCSpaceType(((CollectionType)clrType).TypeUsage.EdmType);
339                 cdmType = new CollectionType(elemType);
340             }
341             else if (Helper.IsRowType(clrType))
342             {
343                 List<EdmProperty> cdmProperties = new List<EdmProperty>();
344                 foreach (EdmProperty column in ((RowType)clrType).Properties)
345                 {
346                     EdmType cdmPropertyType = ConvertOSpaceToCSpaceType(column.TypeUsage.EdmType);
347                     EdmProperty cdmPorperty = new EdmProperty(column.Name, TypeUsage.Create(cdmPropertyType));
348                     cdmProperties.Add(cdmPorperty);
349                 }
350                 cdmType = new RowType(cdmProperties, ((RowType)clrType).InitializerMetadata);
351             }
352             else if (Helper.IsRefType(clrType))
353             {
354                 cdmType = new RefType((EntityType)(ConvertOSpaceToCSpaceType(((RefType)clrType).ElementType)));
355             }
356             else
357             {
358                 cdmType = ((ObjectTypeMapping)GetMap(clrType)).EdmType;
359             }
360             Debug.Assert((null != cdmType), "null converted clr type");
361             return cdmType;
362         }
363
364         /// <summary>
365         /// checks if the schemaKey refers to the primitive OC mapping schema and if true, 
366         /// loads the maps between primitive types
367         /// </summary>
368         /// <returns>returns the loaded schema if the schema key refers to a primitive schema</returns>
369         private void LoadPrimitiveMaps() {
370             // Get all the primitive types from the CSpace and create OCMaps for it
371             IEnumerable<PrimitiveType> cspaceTypes = m_edmCollection.GetPrimitiveTypes();
372             foreach (PrimitiveType type in cspaceTypes)
373             {
374                 PrimitiveType ospaceType = m_objectCollection.GetMappedPrimitiveType(type.PrimitiveTypeKind);
375                 Debug.Assert(ospaceType != null, "all primitive type must have been loaded");
376                 this.AddInternalMapping(new ObjectTypeMapping(ospaceType, type));
377             }
378         }
379
380         // Add to the cache. If it is already present, then throw an exception
381         private void AddInternalMapping(ObjectTypeMapping objectMap)
382         {
383             string clrName = objectMap.ClrType.Identity;
384             string cdmName = objectMap.EdmType.Identity;
385             int currIndex = this.Count;
386             //Always assume that the first Map for an associated map being added is
387             //the default map for primitive type. Similarly, row and collection types can collide
388             //because their components are primitive types. For other types,
389             //there should be only one map
390             if (clrTypeIndexes.ContainsKey(clrName))
391             {
392                 if (BuiltInTypeKind.PrimitiveType != objectMap.ClrType.BuiltInTypeKind &&
393                     BuiltInTypeKind.RowType != objectMap.ClrType.BuiltInTypeKind &&
394                     BuiltInTypeKind.CollectionType != objectMap.ClrType.BuiltInTypeKind)
395                 {
396                     throw new MappingException(System.Data.Entity.Strings.Mapping_Duplicate_Type(clrName));
397                 }
398             }
399             else
400             {
401                 clrTypeIndexes.Add(clrName, currIndex);
402             }
403             if (cdmTypeIndexes.ContainsKey(cdmName))
404             {
405                 if (BuiltInTypeKind.PrimitiveType != objectMap.EdmType.BuiltInTypeKind &&
406                     BuiltInTypeKind.RowType != objectMap.EdmType.BuiltInTypeKind &&
407                     BuiltInTypeKind.CollectionType != objectMap.EdmType.BuiltInTypeKind)
408                 {
409                     throw new MappingException(System.Data.Entity.Strings.Mapping_Duplicate_Type(clrName));
410                 }
411             }
412             else
413             {
414                 cdmTypeIndexes.Add(cdmName, currIndex);
415             }
416             objectMap.DataSpace = DataSpace.OCSpace;
417             base.AddInternal (objectMap);
418         }
419
420         /// <summary>
421         /// The method fills up the children of ObjectMapping. It goes through the
422         /// members in CDM type and finds the member in Object space with the same name 
423         /// and creates a member map between them. These member maps are added
424         /// as children of the object mapping.
425         /// </summary>
426         /// <param name="cdmType"></param>
427         /// <param name="objectType"></param>
428         /// <param name="ocItemCollection"></param>
429         internal static ObjectTypeMapping LoadObjectMapping(EdmType cdmType, EdmType objectType, DefaultObjectMappingItemCollection ocItemCollection)
430         {
431             Dictionary<string, ObjectTypeMapping> typeMappings = new Dictionary<string, ObjectTypeMapping>(StringComparer.Ordinal);
432             ObjectTypeMapping typeMapping = LoadObjectMapping(cdmType, objectType, ocItemCollection, typeMappings);
433
434             // If DefaultOCMappingItemCollection is not null, add all the type mappings to the item collection
435             if (ocItemCollection != null)
436             {
437                 foreach (ObjectTypeMapping map in typeMappings.Values)
438                 {
439                     ocItemCollection.AddInternalMapping(map);
440                 }
441             }
442
443             return typeMapping;
444         }
445
446         private static ObjectTypeMapping LoadObjectMapping(EdmType edmType, EdmType objectType, DefaultObjectMappingItemCollection ocItemCollection,
447             Dictionary<string, ObjectTypeMapping> typeMappings)
448         {
449             Debug.Assert((edmType != null) && (objectType != null));
450             Debug.Assert((edmType.BuiltInTypeKind == objectType.BuiltInTypeKind), "The BuiltInTypeKind must be same in LoadObjectMapping");
451
452             if (Helper.IsEnumType(edmType) ^ Helper.IsEnumType(objectType))
453             {
454                 throw new MappingException(System.Data.Entity.Strings.Mapping_EnumTypeMappingToNonEnumType(edmType.FullName, objectType.FullName));
455             }
456
457             // Check if both the types are abstract or both of them are not
458             if (edmType.Abstract != objectType.Abstract)
459             {
460                 throw new MappingException(System.Data.Entity.Strings.Mapping_AbstractTypeMappingToNonAbstractType(edmType.FullName, objectType.FullName));
461             }
462
463             ObjectTypeMapping objectTypeMapping = new ObjectTypeMapping(objectType, edmType);
464             typeMappings.Add(edmType.FullName, objectTypeMapping);
465
466             if (Helper.IsEntityType(edmType) || Helper.IsComplexType(edmType))
467             {
468                 LoadEntityTypeOrComplexTypeMapping(objectTypeMapping, edmType, objectType, ocItemCollection, typeMappings);
469             }
470             else if (Helper.IsEnumType(edmType))
471             {
472                 ValidateEnumTypeMapping((EnumType)edmType, (EnumType)objectType);
473             }
474             else
475             {
476                 Debug.Assert(Helper.IsAssociationType(edmType));
477
478                 LoadAssociationTypeMapping(objectTypeMapping, edmType, objectType, ocItemCollection, typeMappings);
479             }
480
481             return objectTypeMapping;
482         }
483
484         /// <summary>
485         /// Tries and get the mapping ospace member for the given edmMember and the ospace type
486         /// </summary>
487         /// <param name="edmMember"></param>
488         /// <param name="objectType"></param>
489         /// <returns></returns
490         private static EdmMember GetObjectMember(EdmMember edmMember, StructuralType objectType)
491         {
492             // Assuming that we will have a single member in O-space for a member in C space
493             EdmMember objectMember;
494             if (!objectType.Members.TryGetValue(edmMember.Name, false/*ignoreCase*/, out objectMember))
495             {
496                 throw new MappingException(System.Data.Entity.Strings.Mapping_Default_OCMapping_Clr_Member(
497                           edmMember.Name, edmMember.DeclaringType.FullName, objectType.FullName));
498             }
499
500             return objectMember;
501         }
502
503         private static void ValidateMembersMatch(EdmMember edmMember, EdmMember objectMember)
504         {
505             Debug.Assert(edmMember.DeclaringType.DataSpace == DataSpace.CSpace, "the cspace member is not on a cspace type");
506             Debug.Assert(objectMember.DeclaringType.DataSpace == DataSpace.OSpace, "the ospace member is not on a cspace type");
507
508             // Make sure the property type is the same
509             if (edmMember.BuiltInTypeKind != objectMember.BuiltInTypeKind)
510             {
511                 throw new MappingException(System.Data.Entity.Strings.Mapping_Default_OCMapping_MemberKind_Mismatch(
512                           edmMember.Name, edmMember.DeclaringType.FullName, edmMember.BuiltInTypeKind,
513                           objectMember.Name, objectMember.DeclaringType.FullName, objectMember.BuiltInTypeKind));
514             }
515
516             // Make sure the member type is the same
517             if (edmMember.TypeUsage.EdmType.BuiltInTypeKind != objectMember.TypeUsage.EdmType.BuiltInTypeKind)
518             {
519                 // use EntityRes.GetString(EntityRes. instead of Strings. because the generated method does not
520                 // include all string parameters (6 rather than 8)
521                 throw new MappingException(EntityRes.GetString(EntityRes.Mapping_Default_OCMapping_Member_Type_Mismatch,
522                     edmMember.TypeUsage.EdmType.Name, edmMember.TypeUsage.EdmType.BuiltInTypeKind, edmMember.Name, edmMember.DeclaringType.FullName,
523                     objectMember.TypeUsage.EdmType.Name, objectMember.TypeUsage.EdmType.BuiltInTypeKind, objectMember.Name, objectMember.DeclaringType.FullName));
524             }
525
526             if (Helper.IsPrimitiveType(edmMember.TypeUsage.EdmType))
527             {
528                 PrimitiveType memberType = Helper.GetSpatialNormalizedPrimitiveType(edmMember.TypeUsage.EdmType);
529
530                 //We expect the CLR prmitive type and their corresponding EDM primitive types to have the same primitive type kind( atleast for now)
531                 if (memberType.PrimitiveTypeKind != ((PrimitiveType)objectMember.TypeUsage.EdmType).PrimitiveTypeKind)
532                 {
533                     
534                     throw new MappingException(System.Data.Entity.Strings.Mapping_Default_OCMapping_Invalid_MemberType(
535                               edmMember.TypeUsage.EdmType.FullName, edmMember.Name, edmMember.DeclaringType.FullName,
536                               objectMember.TypeUsage.EdmType.FullName, objectMember.Name, objectMember.DeclaringType.FullName));
537                 }
538             }
539             else if (Helper.IsEnumType(edmMember.TypeUsage.EdmType))
540             {
541                 Debug.Assert(
542                     Helper.IsEnumType(objectMember.TypeUsage.EdmType), 
543                     "Both types are expected to by EnumTypes. For non-matching types we should have already thrown.");
544
545                 ValidateEnumTypeMapping((EnumType)edmMember.TypeUsage.EdmType, (EnumType)objectMember.TypeUsage.EdmType);
546             }
547             else
548             {
549                 EdmType edmMemberType;
550                 EdmType objectMemberType;
551
552                 if (BuiltInTypeKind.AssociationEndMember == edmMember.BuiltInTypeKind)
553                 {
554                     edmMemberType = ((RefType)edmMember.TypeUsage.EdmType).ElementType;
555                     objectMemberType = ((RefType)objectMember.TypeUsage.EdmType).ElementType;
556                 }
557                 else if (BuiltInTypeKind.NavigationProperty == edmMember.BuiltInTypeKind &&
558                          Helper.IsCollectionType(edmMember.TypeUsage.EdmType))
559                 {
560                     edmMemberType = ((CollectionType)edmMember.TypeUsage.EdmType).TypeUsage.EdmType;
561                     objectMemberType = ((CollectionType)objectMember.TypeUsage.EdmType).TypeUsage.EdmType;
562                 }
563                 else
564                 {
565                     edmMemberType = edmMember.TypeUsage.EdmType;
566                     objectMemberType = objectMember.TypeUsage.EdmType;
567                 }
568
569                 if (edmMemberType.Identity != ObjectItemCollection.TryGetMappingCSpaceTypeIdentity(objectMemberType))
570                 {
571                     throw new MappingException(System.Data.Entity.Strings.Mapping_Default_OCMapping_Invalid_MemberType(
572                               edmMember.TypeUsage.EdmType.FullName, edmMember.Name, edmMember.DeclaringType.FullName,
573                               objectMember.TypeUsage.EdmType.FullName, objectMember.Name, objectMember.DeclaringType.FullName));
574                 }
575             }
576
577         }
578
579         /// <summary>
580         /// Validates the scalar property on the cspace side and ospace side and creates a new 
581         /// ObjectPropertyMapping, if everything maps property
582         /// </summary>
583         /// <param name="edmProperty"></param>
584         /// <param name="objectProperty"></param>
585         /// <returns></returns>
586         private static ObjectPropertyMapping LoadScalarPropertyMapping(EdmProperty edmProperty, EdmProperty objectProperty)
587         {
588             Debug.Assert(
589                 Helper.IsScalarType(edmProperty.TypeUsage.EdmType), 
590                 "Only edm scalar properties expected");
591             Debug.Assert(
592                 Helper.IsScalarType(objectProperty.TypeUsage.EdmType), 
593                 "Only object scalar properties expected");
594
595             return new ObjectPropertyMapping(edmProperty, objectProperty);
596         }
597
598         /// <summary>
599         /// Load the entity type or complex type mapping
600         /// </summary>
601         /// <param name="objectMapping"></param>
602         /// <param name="edmType"></param>
603         /// <param name="objectType"></param>
604         /// <param name="ocItemCollection">
605         /// <param name="typeMappings"></param></param>
606         private static void LoadEntityTypeOrComplexTypeMapping(ObjectTypeMapping objectMapping, EdmType edmType, EdmType objectType,
607             DefaultObjectMappingItemCollection ocItemCollection, Dictionary<string, ObjectTypeMapping> typeMappings)
608         {
609             Debug.Assert(edmType.BuiltInTypeKind == BuiltInTypeKind.EntityType ||
610                     edmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType, 
611                     "Expected Type Encountered in LoadEntityTypeOrComplexTypeMapping");
612             Debug.Assert((edmType.BuiltInTypeKind == objectType.BuiltInTypeKind), "The BuiltInTypeKind must be same in LoadEntityTypeOrComplexTypeMapping");
613
614             StructuralType cdmStructuralType = (StructuralType)edmType;
615             StructuralType objectStructuralType = (StructuralType)objectType;
616
617             ValidateAllMembersAreMapped(cdmStructuralType, objectStructuralType);
618
619             //Go through the CDMMembers and find the corresponding member in Object space
620             //and create a member map.
621             foreach (EdmMember edmMember in cdmStructuralType.Members)
622             {
623
624                 EdmMember objectMember = GetObjectMember(edmMember, objectStructuralType);
625                 ValidateMembersMatch(edmMember, objectMember);
626
627                 if (Helper.IsEdmProperty(edmMember))
628                 {
629
630                     EdmProperty edmPropertyMember = (EdmProperty)edmMember;
631                     EdmProperty edmPropertyObject = (EdmProperty)objectMember;
632
633                     //Depending on the type of member load the member mapping i.e. For complex
634                     //members we have to go in and load the child members of the Complex type.
635                     if (Helper.IsComplexType(edmMember.TypeUsage.EdmType))
636                     {
637                         objectMapping.AddMemberMap(
638                             LoadComplexMemberMapping(edmPropertyMember, edmPropertyObject, ocItemCollection, typeMappings));
639                     }
640                     else
641                     {
642                         objectMapping.AddMemberMap(
643                             LoadScalarPropertyMapping(edmPropertyMember, edmPropertyObject));
644                     }
645                 }
646                 else
647                 {
648                     Debug.Assert(edmMember.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty, "Unexpected Property type encountered");
649
650                     // For navigation properties, we need to make sure the relationship type on the navigation property is mapped
651                     NavigationProperty navigationProperty = (NavigationProperty)edmMember;
652                     NavigationProperty objectNavigationProperty = (NavigationProperty)objectMember;
653                     LoadTypeMapping(navigationProperty.RelationshipType, objectNavigationProperty.RelationshipType, ocItemCollection, typeMappings);
654
655                     objectMapping.AddMemberMap(new ObjectNavigationPropertyMapping(navigationProperty, objectNavigationProperty));
656                 }
657
658             }
659         }
660
661         private static void ValidateAllMembersAreMapped(StructuralType cdmStructuralType, StructuralType objectStructuralType)
662         {
663             Debug.Assert(cdmStructuralType.BuiltInTypeKind == objectStructuralType.BuiltInTypeKind, "the types must be the same");
664
665             // error if they don't have the same required members, or if
666             // some object concepts don't exist in cspace (it is ok if the ospace is missing some cspace concepts)
667             if (cdmStructuralType.Members.Count != objectStructuralType.Members.Count)
668             {
669                 throw new MappingException(System.Data.Entity.Strings.Mapping_Default_OCMapping_Member_Count_Mismatch(
670                           cdmStructuralType.FullName, objectStructuralType.FullName));
671             }
672
673             foreach (EdmMember member in objectStructuralType.Members)
674             {
675
676                 if(!cdmStructuralType.Members.Contains(member.Identity))
677                 {
678                     throw new MappingException(System.Data.Entity.Strings.Mapping_Default_OCMapping_Clr_Member2(
679                             member.Name, objectStructuralType.FullName, cdmStructuralType.FullName));
680                 }
681             }
682         }
683
684         /// <summary>
685         /// Validates whether CSpace enum type and OSpace enum type match.
686         /// </summary>
687         /// <param name="edmEnumType">CSpace enum type.</param>
688         /// <param name="objectEnumType">OSpace enum type.</param>
689         private static void ValidateEnumTypeMapping(EnumType edmEnumType, EnumType objectEnumType)
690         {
691             Debug.Assert(edmEnumType != null, "edmEnumType != null");
692             Debug.Assert(Helper.IsPrimitiveType(edmEnumType.UnderlyingType));
693             Debug.Assert(Helper.IsSupportedEnumUnderlyingType(edmEnumType.UnderlyingType.PrimitiveTypeKind));
694
695             Debug.Assert(objectEnumType != null, "objectEnumType != null");
696             Debug.Assert(Helper.IsPrimitiveType(objectEnumType.UnderlyingType));
697             Debug.Assert(Helper.IsSupportedEnumUnderlyingType(objectEnumType.UnderlyingType.PrimitiveTypeKind));
698
699             if (edmEnumType.UnderlyingType.PrimitiveTypeKind != objectEnumType.UnderlyingType.PrimitiveTypeKind)
700             {
701                 throw new MappingException(
702                     System.Data.Entity.Strings.Mapping_Enum_OCMapping_UnderlyingTypesMismatch(
703                         edmEnumType.UnderlyingType.Name, 
704                         edmEnumType.FullName,
705                         objectEnumType.UnderlyingType.Name,
706                         objectEnumType.FullName));
707             }
708
709             // EnumMember.Value is just a number so sorting by value is faster than by the name. 
710             // The drawback is that there can be multiple members with the same value. To break 
711             // the tie we need to sort by name after sorting by value.
712             var edmEnumTypeMembersSortedEnumerator =
713                 edmEnumType.Members.OrderBy(m => Convert.ToInt64(m.Value, CultureInfo.InvariantCulture)).ThenBy(m => m.Name).GetEnumerator();
714             var objectEnumTypeMembersSortedEnumerator =
715                 objectEnumType.Members.OrderBy(m => Convert.ToInt64(m.Value, CultureInfo.InvariantCulture)).ThenBy(m => m.Name).GetEnumerator();
716
717             if (edmEnumTypeMembersSortedEnumerator.MoveNext())
718             {
719                 while (objectEnumTypeMembersSortedEnumerator.MoveNext())
720                 {
721                     if (edmEnumTypeMembersSortedEnumerator.Current.Name == objectEnumTypeMembersSortedEnumerator.Current.Name &&
722                         edmEnumTypeMembersSortedEnumerator.Current.Value.Equals(objectEnumTypeMembersSortedEnumerator.Current.Value))
723                     {
724                         if (!edmEnumTypeMembersSortedEnumerator.MoveNext())
725                         {
726                             return;
727                         }
728                     }
729                 }
730
731                 throw new MappingException(
732                     System.Data.Entity.Strings.Mapping_Enum_OCMapping_MemberMismatch(
733                         objectEnumType.FullName,
734                         edmEnumTypeMembersSortedEnumerator.Current.Name,
735                         edmEnumTypeMembersSortedEnumerator.Current.Value,
736                         edmEnumType.FullName));
737             }
738         }
739
740         /// <summary>
741         /// Loads Association Type Mapping
742         /// </summary>
743         /// <param name="objectMapping"></param>
744         /// <param name="edmType"></param>
745         /// <param name="objectType"></param>
746         /// <param name="ocItemCollection"></param>
747         /// <param name="typeMappings"></param>
748         private static void LoadAssociationTypeMapping(ObjectTypeMapping objectMapping, EdmType edmType, EdmType objectType,
749             DefaultObjectMappingItemCollection ocItemCollection, Dictionary<string, ObjectTypeMapping> typeMappings)
750         {
751             Debug.Assert(edmType.BuiltInTypeKind == BuiltInTypeKind.AssociationType, "Expected Type Encountered in LoadAssociationTypeMapping");
752             Debug.Assert((edmType.BuiltInTypeKind == objectType.BuiltInTypeKind), "The BuiltInTypeKind must be same in LoadAssociationTypeMapping");
753
754             AssociationType association = (AssociationType)edmType;
755             AssociationType objectAssociation = (AssociationType)objectType;
756
757             foreach (AssociationEndMember edmEnd in association.AssociationEndMembers)
758             {
759                 AssociationEndMember objectEnd = (AssociationEndMember)GetObjectMember(edmEnd, objectAssociation);
760                 ValidateMembersMatch(edmEnd, objectEnd);
761
762                 if (edmEnd.RelationshipMultiplicity != objectEnd.RelationshipMultiplicity)
763                 {
764                     throw new MappingException(System.Data.Entity.Strings.Mapping_Default_OCMapping_MultiplicityMismatch(
765                         edmEnd.RelationshipMultiplicity, edmEnd.Name, association.FullName,
766                         objectEnd.RelationshipMultiplicity, objectEnd.Name, objectAssociation.FullName));
767                 }
768
769                 Debug.Assert(edmEnd.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.RefType, "Ends must be of Ref type");
770
771                 // GetMap for the entity types for the ends of the relationship type to make sure
772                 // the entity type mentioned are valid
773                 LoadTypeMapping(((RefType)edmEnd.TypeUsage.EdmType).ElementType,
774                                 ((RefType)objectEnd.TypeUsage.EdmType).ElementType, ocItemCollection, typeMappings);
775
776                 objectMapping.AddMemberMap(new ObjectAssociationEndMapping(edmEnd, objectEnd));
777             }
778         }
779
780         /// <summary>
781         /// The method loads the EdmMember mapping for complex members.
782         /// It goes through the CDM members of the Complex Cdm type and
783         /// tries to find the corresponding members in Complex Clr type.
784         /// </summary>
785         /// <param name="containingEdmMember"></param>
786         /// <param name="containingClrMember"></param>
787         /// <param name="ocItemCollection"></param>
788         /// <param name="typeMappings"></param>
789         /// <returns></returns>
790         private static ObjectComplexPropertyMapping LoadComplexMemberMapping(EdmProperty containingEdmMember, EdmProperty containingClrMember,
791             DefaultObjectMappingItemCollection ocItemCollection, Dictionary<string, ObjectTypeMapping> typeMappings)
792         {
793             Debug.Assert(containingEdmMember.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType, "edm member declaringType must be of complexType");
794             Debug.Assert(containingClrMember.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType, "clr member declaringType must be of complexType");
795
796             ComplexType edmComplexType = (ComplexType)containingEdmMember.TypeUsage.EdmType;
797             ComplexType objectComplexType = (ComplexType)containingClrMember.TypeUsage.EdmType;
798
799             // Get the type mapping for the complex type
800             ObjectTypeMapping complexTypeMapping = LoadTypeMapping(edmComplexType, objectComplexType, ocItemCollection, typeMappings);
801
802             //Go through the CDMMembers and find the corresponding member in Object space
803             //and create a member map.
804             return new ObjectComplexPropertyMapping(containingEdmMember, containingClrMember, complexTypeMapping);
805         }
806
807         private static ObjectTypeMapping LoadTypeMapping(EdmType edmType, EdmType objectType, 
808             DefaultObjectMappingItemCollection ocItemCollection, Dictionary<string, ObjectTypeMapping> typeMappings)
809         {
810             ObjectTypeMapping objectTypeMapping;
811
812             //First, check in the type mappings to find out if the mapping is already present
813             if (typeMappings.TryGetValue(edmType.FullName, out objectTypeMapping))
814             {
815                 return objectTypeMapping;
816             }
817
818             if (ocItemCollection != null)
819             {
820                 ObjectTypeMapping typeMapping;
821
822                 if (ocItemCollection.ContainsMap(edmType, out typeMapping))
823                 {
824                     return (ObjectTypeMapping)typeMapping;
825                 }
826             }
827
828             // If the type mapping is not already loaded, then load it
829             return LoadObjectMapping(edmType, objectType, ocItemCollection, typeMappings);
830         }
831
832         private bool ContainsMap(GlobalItem cspaceItem, out ObjectTypeMapping map)
833         {
834             Debug.Assert(cspaceItem.DataSpace == DataSpace.CSpace, "ContainsMap: It must be a CSpace item");
835             int index;
836             if (cdmTypeIndexes.TryGetValue(cspaceItem.Identity, out index))
837             {
838                 map = (ObjectTypeMapping)this[index];
839                 return true;
840             }
841
842             map = null;
843             return false;
844         }
845
846         #endregion
847     }
848 }