1 //---------------------------------------------------------------------
2 // <copyright file="DefaultObjectMappingItemCollection.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
8 //---------------------------------------------------------------------
10 namespace System.Data.Mapping {
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;
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.
27 internal class DefaultObjectMappingItemCollection : MappingItemCollection {
30 /// Constrcutor to create an instance of DefaultObjectMappingItemCollection.
31 /// To start with we will create a Schema under which maps will be created.
33 /// <param name="edmCollection"></param>
34 /// <param name="objectCollection"></param>
35 public DefaultObjectMappingItemCollection(EdmItemCollection edmCollection,
36 ObjectItemCollection objectCollection) : base(DataSpace.OCSpace)
38 EntityUtil.CheckArgumentNull(edmCollection, "edmCollection");
39 EntityUtil.CheckArgumentNull(objectCollection, "objectCollection");
40 this.m_edmCollection = edmCollection;
41 this.m_objectCollection = objectCollection;
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
55 /// Search for a Mapping metadata with the specified type key.
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)
64 if (!this.TryGetMap(identity, typeSpace, ignoreCase, out map))
66 throw new InvalidOperationException(System.Data.Entity.Strings.Mapping_Object_InvalidType(identity));
72 /// Search for a Mapping metadata with the specified type key.
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)
81 EdmType cdmType = null;
82 EdmType clrType = null;
83 if (typeSpace == DataSpace.CSpace)
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))
94 identity = cdmType.Identity;
98 if (cdmTypeIndexes.TryGetValue(identity, out index))
100 map = (Map)this[index];
104 if (cdmType != null ||
105 m_edmCollection.TryGetItem<EdmType>(identity, ignoreCase, out cdmType))
107 // If the mapping is not already loaded, then get the mapping ospace type
108 m_objectCollection.TryGetOSpaceType(cdmType, out clrType);
111 else if (typeSpace == DataSpace.OSpace)
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))
122 identity = clrType.Identity;
126 if (clrTypeIndexes.TryGetValue(identity, out index))
128 map = (Map)this[index];
132 if (clrType != null ||
133 m_objectCollection.TryGetItem<EdmType>(identity, ignoreCase, out clrType))
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);
141 if ((clrType == null) || (cdmType == null))
148 map = this.GetDefaultMapping(cdmType, clrType);
154 /// Search for a Mapping metadata with the specified type key.
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)
161 return this.GetMap(identity, typeSpace, false /*ignoreCase*/);
165 /// Search for a Mapping metadata with the specified type key.
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)
173 return this.TryGetMap(identity, typeSpace, false /*ignoreCase*/, out map);
177 /// Search for a Mapping metadata with the specified type key.
179 /// <param name="item"></param>
180 internal override Map GetMap(GlobalItem item)
182 EntityUtil.CheckArgumentNull(item, "item");
184 if (!this.TryGetMap(item, out map))
186 throw new InvalidOperationException(System.Data.Entity.Strings.Mapping_Object_InvalidType(item.Identity));
192 /// Search for a Mapping metadata with the specified type key.
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)
205 DataSpace typeSpace = item.DataSpace;
207 //For transient types just create a map on fly and return
208 EdmType edmType = item as EdmType;
211 if (Helper.IsTransientType(edmType))
213 map = GetOCMapForTransientType(edmType, typeSpace);
224 return this.TryGetMap(item.Identity, typeSpace, out map);
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.
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);
240 private Map GetOCMapForTransientType(EdmType edmType, DataSpace typeSpace)
242 Debug.Assert(typeSpace == DataSpace.CSpace || typeSpace == DataSpace.OSpace || Helper.IsRowType(edmType) || Helper.IsCollectionType(edmType));
243 EdmType clrType = null;
244 EdmType cdmType = null;
246 if (typeSpace != DataSpace.OSpace)
248 if (cdmTypeIndexes.TryGetValue(edmType.Identity, out index))
250 return (Map)this[index];
255 clrType = ConvertCSpaceToOSpaceType(edmType);
258 else if (typeSpace == DataSpace.OSpace)
260 if (clrTypeIndexes.TryGetValue(edmType.Identity, out index))
262 return (Map)this[index];
267 cdmType = ConvertOSpaceToCSpaceType(clrType);
271 ObjectTypeMapping typeMapping = new ObjectTypeMapping(clrType, cdmType);
272 if (BuiltInTypeKind.RowType == edmType.BuiltInTypeKind)
274 RowType clrRowType = (RowType)clrType;
275 RowType edmRowType = (RowType)cdmType;
277 Debug.Assert(clrRowType.Properties.Count == edmRowType.Properties.Count, "Property count mismatch");
278 for (int idx = 0; idx < clrRowType.Properties.Count; idx++)
280 typeMapping.AddMemberMap(new ObjectPropertyMapping(edmRowType.Properties[idx], clrRowType.Properties[idx]));
283 if ( (!cdmTypeIndexes.ContainsKey(cdmType.Identity)) && (!clrTypeIndexes.ContainsKey(clrType.Identity)) )
285 AddInternalMapping(typeMapping);
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)
295 EdmType clrType = null;
297 if (Helper.IsCollectionType(cdmType))
299 EdmType elemType = ConvertCSpaceToOSpaceType(((CollectionType)cdmType).TypeUsage.EdmType);
300 clrType = new CollectionType(elemType);
302 else if (Helper.IsRowType(cdmType))
304 List<EdmProperty> clrProperties = new List<EdmProperty>();
305 foreach (EdmProperty column in ((RowType)cdmType).Properties)
307 EdmType clrPropertyType = ConvertCSpaceToOSpaceType(column.TypeUsage.EdmType);
308 EdmProperty clrProperty = new EdmProperty(column.Name, TypeUsage.Create(clrPropertyType));
309 clrProperties.Add(clrProperty);
311 clrType = new RowType(clrProperties, ((RowType)cdmType).InitializerMetadata);
313 else if (Helper.IsRefType(cdmType))
315 clrType = new RefType((EntityType)ConvertCSpaceToOSpaceType(((RefType)cdmType).ElementType));
317 else if (Helper.IsPrimitiveType(cdmType))
319 clrType = m_objectCollection.GetMappedPrimitiveType(((PrimitiveType)cdmType).PrimitiveTypeKind);
323 clrType = ((ObjectTypeMapping)GetMap(cdmType)).ClrType;
325 Debug.Assert((null != clrType), "null converted clr type");
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)
334 EdmType cdmType = null;
336 if (Helper.IsCollectionType(clrType))
338 EdmType elemType = ConvertOSpaceToCSpaceType(((CollectionType)clrType).TypeUsage.EdmType);
339 cdmType = new CollectionType(elemType);
341 else if (Helper.IsRowType(clrType))
343 List<EdmProperty> cdmProperties = new List<EdmProperty>();
344 foreach (EdmProperty column in ((RowType)clrType).Properties)
346 EdmType cdmPropertyType = ConvertOSpaceToCSpaceType(column.TypeUsage.EdmType);
347 EdmProperty cdmPorperty = new EdmProperty(column.Name, TypeUsage.Create(cdmPropertyType));
348 cdmProperties.Add(cdmPorperty);
350 cdmType = new RowType(cdmProperties, ((RowType)clrType).InitializerMetadata);
352 else if (Helper.IsRefType(clrType))
354 cdmType = new RefType((EntityType)(ConvertOSpaceToCSpaceType(((RefType)clrType).ElementType)));
358 cdmType = ((ObjectTypeMapping)GetMap(clrType)).EdmType;
360 Debug.Assert((null != cdmType), "null converted clr type");
365 /// checks if the schemaKey refers to the primitive OC mapping schema and if true,
366 /// loads the maps between primitive types
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)
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));
380 // Add to the cache. If it is already present, then throw an exception
381 private void AddInternalMapping(ObjectTypeMapping objectMap)
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))
392 if (BuiltInTypeKind.PrimitiveType != objectMap.ClrType.BuiltInTypeKind &&
393 BuiltInTypeKind.RowType != objectMap.ClrType.BuiltInTypeKind &&
394 BuiltInTypeKind.CollectionType != objectMap.ClrType.BuiltInTypeKind)
396 throw new MappingException(System.Data.Entity.Strings.Mapping_Duplicate_Type(clrName));
401 clrTypeIndexes.Add(clrName, currIndex);
403 if (cdmTypeIndexes.ContainsKey(cdmName))
405 if (BuiltInTypeKind.PrimitiveType != objectMap.EdmType.BuiltInTypeKind &&
406 BuiltInTypeKind.RowType != objectMap.EdmType.BuiltInTypeKind &&
407 BuiltInTypeKind.CollectionType != objectMap.EdmType.BuiltInTypeKind)
409 throw new MappingException(System.Data.Entity.Strings.Mapping_Duplicate_Type(clrName));
414 cdmTypeIndexes.Add(cdmName, currIndex);
416 objectMap.DataSpace = DataSpace.OCSpace;
417 base.AddInternal (objectMap);
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.
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)
431 Dictionary<string, ObjectTypeMapping> typeMappings = new Dictionary<string, ObjectTypeMapping>(StringComparer.Ordinal);
432 ObjectTypeMapping typeMapping = LoadObjectMapping(cdmType, objectType, ocItemCollection, typeMappings);
434 // If DefaultOCMappingItemCollection is not null, add all the type mappings to the item collection
435 if (ocItemCollection != null)
437 foreach (ObjectTypeMapping map in typeMappings.Values)
439 ocItemCollection.AddInternalMapping(map);
446 private static ObjectTypeMapping LoadObjectMapping(EdmType edmType, EdmType objectType, DefaultObjectMappingItemCollection ocItemCollection,
447 Dictionary<string, ObjectTypeMapping> typeMappings)
449 Debug.Assert((edmType != null) && (objectType != null));
450 Debug.Assert((edmType.BuiltInTypeKind == objectType.BuiltInTypeKind), "The BuiltInTypeKind must be same in LoadObjectMapping");
452 if (Helper.IsEnumType(edmType) ^ Helper.IsEnumType(objectType))
454 throw new MappingException(System.Data.Entity.Strings.Mapping_EnumTypeMappingToNonEnumType(edmType.FullName, objectType.FullName));
457 // Check if both the types are abstract or both of them are not
458 if (edmType.Abstract != objectType.Abstract)
460 throw new MappingException(System.Data.Entity.Strings.Mapping_AbstractTypeMappingToNonAbstractType(edmType.FullName, objectType.FullName));
463 ObjectTypeMapping objectTypeMapping = new ObjectTypeMapping(objectType, edmType);
464 typeMappings.Add(edmType.FullName, objectTypeMapping);
466 if (Helper.IsEntityType(edmType) || Helper.IsComplexType(edmType))
468 LoadEntityTypeOrComplexTypeMapping(objectTypeMapping, edmType, objectType, ocItemCollection, typeMappings);
470 else if (Helper.IsEnumType(edmType))
472 ValidateEnumTypeMapping((EnumType)edmType, (EnumType)objectType);
476 Debug.Assert(Helper.IsAssociationType(edmType));
478 LoadAssociationTypeMapping(objectTypeMapping, edmType, objectType, ocItemCollection, typeMappings);
481 return objectTypeMapping;
485 /// Tries and get the mapping ospace member for the given edmMember and the ospace type
487 /// <param name="edmMember"></param>
488 /// <param name="objectType"></param>
489 /// <returns></returns
490 private static EdmMember GetObjectMember(EdmMember edmMember, StructuralType objectType)
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))
496 throw new MappingException(System.Data.Entity.Strings.Mapping_Default_OCMapping_Clr_Member(
497 edmMember.Name, edmMember.DeclaringType.FullName, objectType.FullName));
503 private static void ValidateMembersMatch(EdmMember edmMember, EdmMember objectMember)
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");
508 // Make sure the property type is the same
509 if (edmMember.BuiltInTypeKind != objectMember.BuiltInTypeKind)
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));
516 // Make sure the member type is the same
517 if (edmMember.TypeUsage.EdmType.BuiltInTypeKind != objectMember.TypeUsage.EdmType.BuiltInTypeKind)
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));
526 if (Helper.IsPrimitiveType(edmMember.TypeUsage.EdmType))
528 PrimitiveType memberType = Helper.GetSpatialNormalizedPrimitiveType(edmMember.TypeUsage.EdmType);
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)
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));
539 else if (Helper.IsEnumType(edmMember.TypeUsage.EdmType))
542 Helper.IsEnumType(objectMember.TypeUsage.EdmType),
543 "Both types are expected to by EnumTypes. For non-matching types we should have already thrown.");
545 ValidateEnumTypeMapping((EnumType)edmMember.TypeUsage.EdmType, (EnumType)objectMember.TypeUsage.EdmType);
549 EdmType edmMemberType;
550 EdmType objectMemberType;
552 if (BuiltInTypeKind.AssociationEndMember == edmMember.BuiltInTypeKind)
554 edmMemberType = ((RefType)edmMember.TypeUsage.EdmType).ElementType;
555 objectMemberType = ((RefType)objectMember.TypeUsage.EdmType).ElementType;
557 else if (BuiltInTypeKind.NavigationProperty == edmMember.BuiltInTypeKind &&
558 Helper.IsCollectionType(edmMember.TypeUsage.EdmType))
560 edmMemberType = ((CollectionType)edmMember.TypeUsage.EdmType).TypeUsage.EdmType;
561 objectMemberType = ((CollectionType)objectMember.TypeUsage.EdmType).TypeUsage.EdmType;
565 edmMemberType = edmMember.TypeUsage.EdmType;
566 objectMemberType = objectMember.TypeUsage.EdmType;
569 if (edmMemberType.Identity != ObjectItemCollection.TryGetMappingCSpaceTypeIdentity(objectMemberType))
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));
580 /// Validates the scalar property on the cspace side and ospace side and creates a new
581 /// ObjectPropertyMapping, if everything maps property
583 /// <param name="edmProperty"></param>
584 /// <param name="objectProperty"></param>
585 /// <returns></returns>
586 private static ObjectPropertyMapping LoadScalarPropertyMapping(EdmProperty edmProperty, EdmProperty objectProperty)
589 Helper.IsScalarType(edmProperty.TypeUsage.EdmType),
590 "Only edm scalar properties expected");
592 Helper.IsScalarType(objectProperty.TypeUsage.EdmType),
593 "Only object scalar properties expected");
595 return new ObjectPropertyMapping(edmProperty, objectProperty);
599 /// Load the entity type or complex type mapping
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)
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");
614 StructuralType cdmStructuralType = (StructuralType)edmType;
615 StructuralType objectStructuralType = (StructuralType)objectType;
617 ValidateAllMembersAreMapped(cdmStructuralType, objectStructuralType);
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)
624 EdmMember objectMember = GetObjectMember(edmMember, objectStructuralType);
625 ValidateMembersMatch(edmMember, objectMember);
627 if (Helper.IsEdmProperty(edmMember))
630 EdmProperty edmPropertyMember = (EdmProperty)edmMember;
631 EdmProperty edmPropertyObject = (EdmProperty)objectMember;
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))
637 objectMapping.AddMemberMap(
638 LoadComplexMemberMapping(edmPropertyMember, edmPropertyObject, ocItemCollection, typeMappings));
642 objectMapping.AddMemberMap(
643 LoadScalarPropertyMapping(edmPropertyMember, edmPropertyObject));
648 Debug.Assert(edmMember.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty, "Unexpected Property type encountered");
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);
655 objectMapping.AddMemberMap(new ObjectNavigationPropertyMapping(navigationProperty, objectNavigationProperty));
661 private static void ValidateAllMembersAreMapped(StructuralType cdmStructuralType, StructuralType objectStructuralType)
663 Debug.Assert(cdmStructuralType.BuiltInTypeKind == objectStructuralType.BuiltInTypeKind, "the types must be the same");
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)
669 throw new MappingException(System.Data.Entity.Strings.Mapping_Default_OCMapping_Member_Count_Mismatch(
670 cdmStructuralType.FullName, objectStructuralType.FullName));
673 foreach (EdmMember member in objectStructuralType.Members)
676 if(!cdmStructuralType.Members.Contains(member.Identity))
678 throw new MappingException(System.Data.Entity.Strings.Mapping_Default_OCMapping_Clr_Member2(
679 member.Name, objectStructuralType.FullName, cdmStructuralType.FullName));
685 /// Validates whether CSpace enum type and OSpace enum type match.
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)
691 Debug.Assert(edmEnumType != null, "edmEnumType != null");
692 Debug.Assert(Helper.IsPrimitiveType(edmEnumType.UnderlyingType));
693 Debug.Assert(Helper.IsSupportedEnumUnderlyingType(edmEnumType.UnderlyingType.PrimitiveTypeKind));
695 Debug.Assert(objectEnumType != null, "objectEnumType != null");
696 Debug.Assert(Helper.IsPrimitiveType(objectEnumType.UnderlyingType));
697 Debug.Assert(Helper.IsSupportedEnumUnderlyingType(objectEnumType.UnderlyingType.PrimitiveTypeKind));
699 if (edmEnumType.UnderlyingType.PrimitiveTypeKind != objectEnumType.UnderlyingType.PrimitiveTypeKind)
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));
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();
717 if (edmEnumTypeMembersSortedEnumerator.MoveNext())
719 while (objectEnumTypeMembersSortedEnumerator.MoveNext())
721 if (edmEnumTypeMembersSortedEnumerator.Current.Name == objectEnumTypeMembersSortedEnumerator.Current.Name &&
722 edmEnumTypeMembersSortedEnumerator.Current.Value.Equals(objectEnumTypeMembersSortedEnumerator.Current.Value))
724 if (!edmEnumTypeMembersSortedEnumerator.MoveNext())
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));
741 /// Loads Association Type Mapping
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)
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");
754 AssociationType association = (AssociationType)edmType;
755 AssociationType objectAssociation = (AssociationType)objectType;
757 foreach (AssociationEndMember edmEnd in association.AssociationEndMembers)
759 AssociationEndMember objectEnd = (AssociationEndMember)GetObjectMember(edmEnd, objectAssociation);
760 ValidateMembersMatch(edmEnd, objectEnd);
762 if (edmEnd.RelationshipMultiplicity != objectEnd.RelationshipMultiplicity)
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));
769 Debug.Assert(edmEnd.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.RefType, "Ends must be of Ref type");
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);
776 objectMapping.AddMemberMap(new ObjectAssociationEndMapping(edmEnd, objectEnd));
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.
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)
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");
796 ComplexType edmComplexType = (ComplexType)containingEdmMember.TypeUsage.EdmType;
797 ComplexType objectComplexType = (ComplexType)containingClrMember.TypeUsage.EdmType;
799 // Get the type mapping for the complex type
800 ObjectTypeMapping complexTypeMapping = LoadTypeMapping(edmComplexType, objectComplexType, ocItemCollection, typeMappings);
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);
807 private static ObjectTypeMapping LoadTypeMapping(EdmType edmType, EdmType objectType,
808 DefaultObjectMappingItemCollection ocItemCollection, Dictionary<string, ObjectTypeMapping> typeMappings)
810 ObjectTypeMapping objectTypeMapping;
812 //First, check in the type mappings to find out if the mapping is already present
813 if (typeMappings.TryGetValue(edmType.FullName, out objectTypeMapping))
815 return objectTypeMapping;
818 if (ocItemCollection != null)
820 ObjectTypeMapping typeMapping;
822 if (ocItemCollection.ContainsMap(edmType, out typeMapping))
824 return (ObjectTypeMapping)typeMapping;
828 // If the type mapping is not already loaded, then load it
829 return LoadObjectMapping(edmType, objectType, ocItemCollection, typeMappings);
832 private bool ContainsMap(GlobalItem cspaceItem, out ObjectTypeMapping map)
834 Debug.Assert(cspaceItem.DataSpace == DataSpace.CSpace, "ContainsMap: It must be a CSpace item");
836 if (cdmTypeIndexes.TryGetValue(cspaceItem.Identity, out index))
838 map = (ObjectTypeMapping)this[index];