|
//---------------------------------------------------------------------
// <copyright file="DefaultObjectMappingItemCollection.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// @owner Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
namespace System.Data.Mapping {
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Metadata.Edm;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
/// <summary>
/// The class creates a default OCMapping between a TypeMetadata in O space
/// and an TypeMetadata in Edm space. The loader expects that for each member in
/// C space type there exists a member in O space type that has the same name. The member maps will be stored in
/// C space member order.
/// </summary>
internal class DefaultObjectMappingItemCollection : MappingItemCollection {
#region Constructors
/// <summary>
/// Constrcutor to create an instance of DefaultObjectMappingItemCollection.
/// To start with we will create a Schema under which maps will be created.
/// </summary>
/// <param name="edmCollection"></param>
/// <param name="objectCollection"></param>
public DefaultObjectMappingItemCollection(EdmItemCollection edmCollection,
ObjectItemCollection objectCollection) : base(DataSpace.OCSpace)
{
EntityUtil.CheckArgumentNull(edmCollection, "edmCollection");
EntityUtil.CheckArgumentNull(objectCollection, "objectCollection");
this.m_edmCollection = edmCollection;
this.m_objectCollection = objectCollection;
LoadPrimitiveMaps();
}
#endregion
#region Fields
private ObjectItemCollection m_objectCollection;
private EdmItemCollection m_edmCollection;
private Dictionary<string, int> clrTypeIndexes = new Dictionary<string, int>(StringComparer.Ordinal); //Indexes into the type mappings collection based on clr type name
private Dictionary<string, int> cdmTypeIndexes = new Dictionary<string, int>(StringComparer.Ordinal); //Indexes into the type mappings collection based on clr type name
#endregion
#region Methods
/// <summary>
/// Search for a Mapping metadata with the specified type key.
/// </summary>
/// <param name="identity">identity of the type</param>
/// <param name="typeSpace">The dataspace that the type for which map needs to be returned belongs to</param>
/// <param name="ignoreCase">true for case-insensitive lookup</param>
/// <exception cref="ArgumentException"> Thrown if mapping space is not valid</exception>
internal override Map GetMap(string identity, DataSpace typeSpace, bool ignoreCase)
{
Map map;
if (!this.TryGetMap(identity, typeSpace, ignoreCase, out map))
{
throw new InvalidOperationException(System.Data.Entity.Strings.Mapping_Object_InvalidType(identity));
}
return map;
}
/// <summary>
/// Search for a Mapping metadata with the specified type key.
/// </summary>
/// <param name="identity">identity of the type</param>
/// <param name="typeSpace">The dataspace that the type for which map needs to be returned belongs to</param>
/// <param name="ignoreCase">true for case-insensitive lookup</param>
/// <param name="map"></param>
/// <returns>Returns false if no match found.</returns>
internal override bool TryGetMap(string identity, DataSpace typeSpace, bool ignoreCase, out Map map)
{
EdmType cdmType = null;
EdmType clrType = null;
if (typeSpace == DataSpace.CSpace)
{
if (ignoreCase)
{
// Get the correct casing of the identity first if we are asked to do ignore case
if (!m_edmCollection.TryGetItem<EdmType>(identity, true, out cdmType))
{
map = null;
return false;
}
identity = cdmType.Identity;
}
int index;
if (cdmTypeIndexes.TryGetValue(identity, out index))
{
map = (Map)this[index];
return true;
}
if (cdmType != null ||
m_edmCollection.TryGetItem<EdmType>(identity, ignoreCase, out cdmType))
{
// If the mapping is not already loaded, then get the mapping ospace type
m_objectCollection.TryGetOSpaceType(cdmType, out clrType);
}
}
else if (typeSpace == DataSpace.OSpace)
{
if (ignoreCase)
{
// Get the correct casing of the identity first if we are asked to do ignore case
if (!m_objectCollection.TryGetItem<EdmType>(identity, true, out clrType))
{
map = null;
return false;
}
identity = clrType.Identity;
}
int index;
if (clrTypeIndexes.TryGetValue(identity, out index))
{
map = (Map)this[index];
return true;
}
if (clrType != null ||
m_objectCollection.TryGetItem<EdmType>(identity, ignoreCase, out clrType))
{
// If the mapping is not already loaded, get the mapping cspace type
string cspaceTypeName = ObjectItemCollection.TryGetMappingCSpaceTypeIdentity(clrType);
m_edmCollection.TryGetItem<EdmType>(cspaceTypeName, out cdmType);
}
}
if ((clrType == null) || (cdmType == null))
{
map = null;
return false;
}
else
{
map = this.GetDefaultMapping(cdmType, clrType);
return true;
}
}
/// <summary>
/// Search for a Mapping metadata with the specified type key.
/// </summary>
/// <param name="identity">identity of the type</param>
/// <param name="typeSpace">The dataspace that the type for which map needs to be returned belongs to</param>
/// <exception cref="ArgumentException"> Thrown if mapping space is not valid</exception>
internal override Map GetMap(string identity, DataSpace typeSpace)
{
return this.GetMap(identity, typeSpace, false /*ignoreCase*/);
}
/// <summary>
/// Search for a Mapping metadata with the specified type key.
/// </summary>
/// <param name="identity">identity of the type</param>
/// <param name="typeSpace">The dataspace that the type for which map needs to be returned belongs to</param>
/// <param name="map"></param>
/// <returns>Returns false if no match found.</returns>
internal override bool TryGetMap(string identity, DataSpace typeSpace, out Map map)
{
return this.TryGetMap(identity, typeSpace, false /*ignoreCase*/, out map);
}
/// <summary>
/// Search for a Mapping metadata with the specified type key.
/// </summary>
/// <param name="item"></param>
internal override Map GetMap(GlobalItem item)
{
EntityUtil.CheckArgumentNull(item, "item");
Map map;
if (!this.TryGetMap(item, out map))
{
throw new InvalidOperationException(System.Data.Entity.Strings.Mapping_Object_InvalidType(item.Identity));
}
return map;
}
/// <summary>
/// Search for a Mapping metadata with the specified type key.
/// </summary>
/// <param name="item"></param>
/// <param name="map"></param>
/// <returns>Returns false if no match found.</returns>
internal override bool TryGetMap(GlobalItem item, out Map map)
{
if (item == null)
{
map = null;
return false;
}
DataSpace typeSpace = item.DataSpace;
//For transient types just create a map on fly and return
EdmType edmType = item as EdmType;
if (edmType != null)
{
if (Helper.IsTransientType(edmType))
{
map = GetOCMapForTransientType(edmType, typeSpace);
if (map != null)
{
return true;
}
else
{
return false;
}
}
}
return this.TryGetMap(item.Identity, typeSpace, out map);
}
/// <summary>
/// The method creates a default mapping between two TypeMetadatas - one in
/// C space and one in O space. The precondition for calling this method is that
/// the type in Object space contains the members with the same name as those of defined in
/// C space. It is not required the otherway.
/// </summary>
/// <param name="cdmType"></param>
/// <param name="clrType"></param>
private Map GetDefaultMapping(EdmType cdmType, EdmType clrType) {
Debug.Assert((cdmType != null) && (clrType != null));
return LoadObjectMapping(cdmType, clrType, this);
}
private Map GetOCMapForTransientType(EdmType edmType, DataSpace typeSpace)
{
Debug.Assert(typeSpace == DataSpace.CSpace || typeSpace == DataSpace.OSpace || Helper.IsRowType(edmType) || Helper.IsCollectionType(edmType));
EdmType clrType = null;
EdmType cdmType = null;
int index = -1;
if (typeSpace != DataSpace.OSpace)
{
if (cdmTypeIndexes.TryGetValue(edmType.Identity, out index))
{
return (Map)this[index];
}
else
{
cdmType = edmType;
clrType = ConvertCSpaceToOSpaceType(edmType);
}
}
else if (typeSpace == DataSpace.OSpace)
{
if (clrTypeIndexes.TryGetValue(edmType.Identity, out index))
{
return (Map)this[index];
}
else
{
clrType = edmType;
cdmType = ConvertOSpaceToCSpaceType(clrType);
}
}
ObjectTypeMapping typeMapping = new ObjectTypeMapping(clrType, cdmType);
if (BuiltInTypeKind.RowType == edmType.BuiltInTypeKind)
{
RowType clrRowType = (RowType)clrType;
RowType edmRowType = (RowType)cdmType;
Debug.Assert(clrRowType.Properties.Count == edmRowType.Properties.Count, "Property count mismatch");
for (int idx = 0; idx < clrRowType.Properties.Count; idx++)
{
typeMapping.AddMemberMap(new ObjectPropertyMapping(edmRowType.Properties[idx], clrRowType.Properties[idx]));
}
}
if ( (!cdmTypeIndexes.ContainsKey(cdmType.Identity)) && (!clrTypeIndexes.ContainsKey(clrType.Identity)) )
{
AddInternalMapping(typeMapping);
}
return typeMapping;
}
/// <summary>Convert CSpace TypeMetadata into OSpace TypeMetadata</summary>
/// <param name="cdmType"></param>
/// <returns>OSpace type metadata</returns>
private EdmType ConvertCSpaceToOSpaceType(EdmType cdmType)
{
EdmType clrType = null;
if (Helper.IsCollectionType(cdmType))
{
EdmType elemType = ConvertCSpaceToOSpaceType(((CollectionType)cdmType).TypeUsage.EdmType);
clrType = new CollectionType(elemType);
}
else if (Helper.IsRowType(cdmType))
{
List<EdmProperty> clrProperties = new List<EdmProperty>();
foreach (EdmProperty column in ((RowType)cdmType).Properties)
{
EdmType clrPropertyType = ConvertCSpaceToOSpaceType(column.TypeUsage.EdmType);
EdmProperty clrProperty = new EdmProperty(column.Name, TypeUsage.Create(clrPropertyType));
clrProperties.Add(clrProperty);
}
clrType = new RowType(clrProperties, ((RowType)cdmType).InitializerMetadata);
}
else if (Helper.IsRefType(cdmType))
{
clrType = new RefType((EntityType)ConvertCSpaceToOSpaceType(((RefType)cdmType).ElementType));
}
else if (Helper.IsPrimitiveType(cdmType))
{
clrType = m_objectCollection.GetMappedPrimitiveType(((PrimitiveType)cdmType).PrimitiveTypeKind);
}
else
{
clrType = ((ObjectTypeMapping)GetMap(cdmType)).ClrType;
}
Debug.Assert((null != clrType), "null converted clr type");
return clrType;
}
/// <summary>Convert CSpace TypeMetadata into OSpace TypeMetadata</summary>
/// <param name="clrType"></param>
/// <returns>OSpace type metadata</returns>
private EdmType ConvertOSpaceToCSpaceType(EdmType clrType)
{
EdmType cdmType = null;
if (Helper.IsCollectionType(clrType))
{
EdmType elemType = ConvertOSpaceToCSpaceType(((CollectionType)clrType).TypeUsage.EdmType);
cdmType = new CollectionType(elemType);
}
else if (Helper.IsRowType(clrType))
{
List<EdmProperty> cdmProperties = new List<EdmProperty>();
foreach (EdmProperty column in ((RowType)clrType).Properties)
{
EdmType cdmPropertyType = ConvertOSpaceToCSpaceType(column.TypeUsage.EdmType);
EdmProperty cdmPorperty = new EdmProperty(column.Name, TypeUsage.Create(cdmPropertyType));
cdmProperties.Add(cdmPorperty);
}
cdmType = new RowType(cdmProperties, ((RowType)clrType).InitializerMetadata);
}
else if (Helper.IsRefType(clrType))
{
cdmType = new RefType((EntityType)(ConvertOSpaceToCSpaceType(((RefType)clrType).ElementType)));
}
else
{
cdmType = ((ObjectTypeMapping)GetMap(clrType)).EdmType;
}
Debug.Assert((null != cdmType), "null converted clr type");
return cdmType;
}
/// <summary>
/// checks if the schemaKey refers to the primitive OC mapping schema and if true,
/// loads the maps between primitive types
/// </summary>
/// <returns>returns the loaded schema if the schema key refers to a primitive schema</returns>
private void LoadPrimitiveMaps() {
// Get all the primitive types from the CSpace and create OCMaps for it
IEnumerable<PrimitiveType> cspaceTypes = m_edmCollection.GetPrimitiveTypes();
foreach (PrimitiveType type in cspaceTypes)
{
PrimitiveType ospaceType = m_objectCollection.GetMappedPrimitiveType(type.PrimitiveTypeKind);
Debug.Assert(ospaceType != null, "all primitive type must have been loaded");
this.AddInternalMapping(new ObjectTypeMapping(ospaceType, type));
}
}
// Add to the cache. If it is already present, then throw an exception
private void AddInternalMapping(ObjectTypeMapping objectMap)
{
string clrName = objectMap.ClrType.Identity;
string cdmName = objectMap.EdmType.Identity;
int currIndex = this.Count;
//Always assume that the first Map for an associated map being added is
//the default map for primitive type. Similarly, row and collection types can collide
//because their components are primitive types. For other types,
//there should be only one map
if (clrTypeIndexes.ContainsKey(clrName))
{
if (BuiltInTypeKind.PrimitiveType != objectMap.ClrType.BuiltInTypeKind &&
BuiltInTypeKind.RowType != objectMap.ClrType.BuiltInTypeKind &&
BuiltInTypeKind.CollectionType != objectMap.ClrType.BuiltInTypeKind)
{
throw new MappingException(System.Data.Entity.Strings.Mapping_Duplicate_Type(clrName));
}
}
else
{
clrTypeIndexes.Add(clrName, currIndex);
}
if (cdmTypeIndexes.ContainsKey(cdmName))
{
if (BuiltInTypeKind.PrimitiveType != objectMap.EdmType.BuiltInTypeKind &&
BuiltInTypeKind.RowType != objectMap.EdmType.BuiltInTypeKind &&
BuiltInTypeKind.CollectionType != objectMap.EdmType.BuiltInTypeKind)
{
throw new MappingException(System.Data.Entity.Strings.Mapping_Duplicate_Type(clrName));
}
}
else
{
cdmTypeIndexes.Add(cdmName, currIndex);
}
objectMap.DataSpace = DataSpace.OCSpace;
base.AddInternal (objectMap);
}
/// <summary>
/// The method fills up the children of ObjectMapping. It goes through the
/// members in CDM type and finds the member in Object space with the same name
/// and creates a member map between them. These member maps are added
/// as children of the object mapping.
/// </summary>
/// <param name="cdmType"></param>
/// <param name="objectType"></param>
/// <param name="ocItemCollection"></param>
internal static ObjectTypeMapping LoadObjectMapping(EdmType cdmType, EdmType objectType, DefaultObjectMappingItemCollection ocItemCollection)
{
Dictionary<string, ObjectTypeMapping> typeMappings = new Dictionary<string, ObjectTypeMapping>(StringComparer.Ordinal);
ObjectTypeMapping typeMapping = LoadObjectMapping(cdmType, objectType, ocItemCollection, typeMappings);
// If DefaultOCMappingItemCollection is not null, add all the type mappings to the item collection
if (ocItemCollection != null)
{
foreach (ObjectTypeMapping map in typeMappings.Values)
{
ocItemCollection.AddInternalMapping(map);
}
}
return typeMapping;
}
private static ObjectTypeMapping LoadObjectMapping(EdmType edmType, EdmType objectType, DefaultObjectMappingItemCollection ocItemCollection,
Dictionary<string, ObjectTypeMapping> typeMappings)
{
Debug.Assert((edmType != null) && (objectType != null));
Debug.Assert((edmType.BuiltInTypeKind == objectType.BuiltInTypeKind), "The BuiltInTypeKind must be same in LoadObjectMapping");
if (Helper.IsEnumType(edmType) ^ Helper.IsEnumType(objectType))
{
throw new MappingException(System.Data.Entity.Strings.Mapping_EnumTypeMappingToNonEnumType(edmType.FullName, objectType.FullName));
}
// Check if both the types are abstract or both of them are not
if (edmType.Abstract != objectType.Abstract)
{
throw new MappingException(System.Data.Entity.Strings.Mapping_AbstractTypeMappingToNonAbstractType(edmType.FullName, objectType.FullName));
}
ObjectTypeMapping objectTypeMapping = new ObjectTypeMapping(objectType, edmType);
typeMappings.Add(edmType.FullName, objectTypeMapping);
if (Helper.IsEntityType(edmType) || Helper.IsComplexType(edmType))
{
LoadEntityTypeOrComplexTypeMapping(objectTypeMapping, edmType, objectType, ocItemCollection, typeMappings);
}
else if (Helper.IsEnumType(edmType))
{
ValidateEnumTypeMapping((EnumType)edmType, (EnumType)objectType);
}
else
{
Debug.Assert(Helper.IsAssociationType(edmType));
LoadAssociationTypeMapping(objectTypeMapping, edmType, objectType, ocItemCollection, typeMappings);
}
return objectTypeMapping;
}
/// <summary>
/// Tries and get the mapping ospace member for the given edmMember and the ospace type
/// </summary>
/// <param name="edmMember"></param>
/// <param name="objectType"></param>
/// <returns></returns
private static EdmMember GetObjectMember(EdmMember edmMember, StructuralType objectType)
{
// Assuming that we will have a single member in O-space for a member in C space
EdmMember objectMember;
if (!objectType.Members.TryGetValue(edmMember.Name, false/*ignoreCase*/, out objectMember))
{
throw new MappingException(System.Data.Entity.Strings.Mapping_Default_OCMapping_Clr_Member(
edmMember.Name, edmMember.DeclaringType.FullName, objectType.FullName));
}
return objectMember;
}
private static void ValidateMembersMatch(EdmMember edmMember, EdmMember objectMember)
{
Debug.Assert(edmMember.DeclaringType.DataSpace == DataSpace.CSpace, "the cspace member is not on a cspace type");
Debug.Assert(objectMember.DeclaringType.DataSpace == DataSpace.OSpace, "the ospace member is not on a cspace type");
// Make sure the property type is the same
if (edmMember.BuiltInTypeKind != objectMember.BuiltInTypeKind)
{
throw new MappingException(System.Data.Entity.Strings.Mapping_Default_OCMapping_MemberKind_Mismatch(
edmMember.Name, edmMember.DeclaringType.FullName, edmMember.BuiltInTypeKind,
objectMember.Name, objectMember.DeclaringType.FullName, objectMember.BuiltInTypeKind));
}
// Make sure the member type is the same
if (edmMember.TypeUsage.EdmType.BuiltInTypeKind != objectMember.TypeUsage.EdmType.BuiltInTypeKind)
{
// use EntityRes.GetString(EntityRes. instead of Strings. because the generated method does not
// include all string parameters (6 rather than 8)
throw new MappingException(EntityRes.GetString(EntityRes.Mapping_Default_OCMapping_Member_Type_Mismatch,
edmMember.TypeUsage.EdmType.Name, edmMember.TypeUsage.EdmType.BuiltInTypeKind, edmMember.Name, edmMember.DeclaringType.FullName,
objectMember.TypeUsage.EdmType.Name, objectMember.TypeUsage.EdmType.BuiltInTypeKind, objectMember.Name, objectMember.DeclaringType.FullName));
}
if (Helper.IsPrimitiveType(edmMember.TypeUsage.EdmType))
{
PrimitiveType memberType = Helper.GetSpatialNormalizedPrimitiveType(edmMember.TypeUsage.EdmType);
//We expect the CLR prmitive type and their corresponding EDM primitive types to have the same primitive type kind( atleast for now)
if (memberType.PrimitiveTypeKind != ((PrimitiveType)objectMember.TypeUsage.EdmType).PrimitiveTypeKind)
{
throw new MappingException(System.Data.Entity.Strings.Mapping_Default_OCMapping_Invalid_MemberType(
edmMember.TypeUsage.EdmType.FullName, edmMember.Name, edmMember.DeclaringType.FullName,
objectMember.TypeUsage.EdmType.FullName, objectMember.Name, objectMember.DeclaringType.FullName));
}
}
else if (Helper.IsEnumType(edmMember.TypeUsage.EdmType))
{
Debug.Assert(
Helper.IsEnumType(objectMember.TypeUsage.EdmType),
"Both types are expected to by EnumTypes. For non-matching types we should have already thrown.");
ValidateEnumTypeMapping((EnumType)edmMember.TypeUsage.EdmType, (EnumType)objectMember.TypeUsage.EdmType);
}
else
{
EdmType edmMemberType;
EdmType objectMemberType;
if (BuiltInTypeKind.AssociationEndMember == edmMember.BuiltInTypeKind)
{
edmMemberType = ((RefType)edmMember.TypeUsage.EdmType).ElementType;
objectMemberType = ((RefType)objectMember.TypeUsage.EdmType).ElementType;
}
else if (BuiltInTypeKind.NavigationProperty == edmMember.BuiltInTypeKind &&
Helper.IsCollectionType(edmMember.TypeUsage.EdmType))
{
edmMemberType = ((CollectionType)edmMember.TypeUsage.EdmType).TypeUsage.EdmType;
objectMemberType = ((CollectionType)objectMember.TypeUsage.EdmType).TypeUsage.EdmType;
}
else
{
edmMemberType = edmMember.TypeUsage.EdmType;
objectMemberType = objectMember.TypeUsage.EdmType;
}
if (edmMemberType.Identity != ObjectItemCollection.TryGetMappingCSpaceTypeIdentity(objectMemberType))
{
throw new MappingException(System.Data.Entity.Strings.Mapping_Default_OCMapping_Invalid_MemberType(
edmMember.TypeUsage.EdmType.FullName, edmMember.Name, edmMember.DeclaringType.FullName,
objectMember.TypeUsage.EdmType.FullName, objectMember.Name, objectMember.DeclaringType.FullName));
}
}
}
/// <summary>
/// Validates the scalar property on the cspace side and ospace side and creates a new
/// ObjectPropertyMapping, if everything maps property
/// </summary>
/// <param name="edmProperty"></param>
/// <param name="objectProperty"></param>
/// <returns></returns>
private static ObjectPropertyMapping LoadScalarPropertyMapping(EdmProperty edmProperty, EdmProperty objectProperty)
{
Debug.Assert(
Helper.IsScalarType(edmProperty.TypeUsage.EdmType),
"Only edm scalar properties expected");
Debug.Assert(
Helper.IsScalarType(objectProperty.TypeUsage.EdmType),
"Only object scalar properties expected");
return new ObjectPropertyMapping(edmProperty, objectProperty);
}
/// <summary>
/// Load the entity type or complex type mapping
/// </summary>
/// <param name="objectMapping"></param>
/// <param name="edmType"></param>
/// <param name="objectType"></param>
/// <param name="ocItemCollection">
/// <param name="typeMappings"></param></param>
private static void LoadEntityTypeOrComplexTypeMapping(ObjectTypeMapping objectMapping, EdmType edmType, EdmType objectType,
DefaultObjectMappingItemCollection ocItemCollection, Dictionary<string, ObjectTypeMapping> typeMappings)
{
Debug.Assert(edmType.BuiltInTypeKind == BuiltInTypeKind.EntityType ||
edmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType,
"Expected Type Encountered in LoadEntityTypeOrComplexTypeMapping");
Debug.Assert((edmType.BuiltInTypeKind == objectType.BuiltInTypeKind), "The BuiltInTypeKind must be same in LoadEntityTypeOrComplexTypeMapping");
StructuralType cdmStructuralType = (StructuralType)edmType;
StructuralType objectStructuralType = (StructuralType)objectType;
ValidateAllMembersAreMapped(cdmStructuralType, objectStructuralType);
//Go through the CDMMembers and find the corresponding member in Object space
//and create a member map.
foreach (EdmMember edmMember in cdmStructuralType.Members)
{
EdmMember objectMember = GetObjectMember(edmMember, objectStructuralType);
ValidateMembersMatch(edmMember, objectMember);
if (Helper.IsEdmProperty(edmMember))
{
EdmProperty edmPropertyMember = (EdmProperty)edmMember;
EdmProperty edmPropertyObject = (EdmProperty)objectMember;
//Depending on the type of member load the member mapping i.e. For complex
//members we have to go in and load the child members of the Complex type.
if (Helper.IsComplexType(edmMember.TypeUsage.EdmType))
{
objectMapping.AddMemberMap(
LoadComplexMemberMapping(edmPropertyMember, edmPropertyObject, ocItemCollection, typeMappings));
}
else
{
objectMapping.AddMemberMap(
LoadScalarPropertyMapping(edmPropertyMember, edmPropertyObject));
}
}
else
{
Debug.Assert(edmMember.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty, "Unexpected Property type encountered");
// For navigation properties, we need to make sure the relationship type on the navigation property is mapped
NavigationProperty navigationProperty = (NavigationProperty)edmMember;
NavigationProperty objectNavigationProperty = (NavigationProperty)objectMember;
LoadTypeMapping(navigationProperty.RelationshipType, objectNavigationProperty.RelationshipType, ocItemCollection, typeMappings);
objectMapping.AddMemberMap(new ObjectNavigationPropertyMapping(navigationProperty, objectNavigationProperty));
}
}
}
private static void ValidateAllMembersAreMapped(StructuralType cdmStructuralType, StructuralType objectStructuralType)
{
Debug.Assert(cdmStructuralType.BuiltInTypeKind == objectStructuralType.BuiltInTypeKind, "the types must be the same");
// error if they don't have the same required members, or if
// some object concepts don't exist in cspace (it is ok if the ospace is missing some cspace concepts)
if (cdmStructuralType.Members.Count != objectStructuralType.Members.Count)
{
throw new MappingException(System.Data.Entity.Strings.Mapping_Default_OCMapping_Member_Count_Mismatch(
cdmStructuralType.FullName, objectStructuralType.FullName));
}
foreach (EdmMember member in objectStructuralType.Members)
{
if(!cdmStructuralType.Members.Contains(member.Identity))
{
throw new MappingException(System.Data.Entity.Strings.Mapping_Default_OCMapping_Clr_Member2(
member.Name, objectStructuralType.FullName, cdmStructuralType.FullName));
}
}
}
/// <summary>
/// Validates whether CSpace enum type and OSpace enum type match.
/// </summary>
/// <param name="edmEnumType">CSpace enum type.</param>
/// <param name="objectEnumType">OSpace enum type.</param>
private static void ValidateEnumTypeMapping(EnumType edmEnumType, EnumType objectEnumType)
{
Debug.Assert(edmEnumType != null, "edmEnumType != null");
Debug.Assert(Helper.IsPrimitiveType(edmEnumType.UnderlyingType));
Debug.Assert(Helper.IsSupportedEnumUnderlyingType(edmEnumType.UnderlyingType.PrimitiveTypeKind));
Debug.Assert(objectEnumType != null, "objectEnumType != null");
Debug.Assert(Helper.IsPrimitiveType(objectEnumType.UnderlyingType));
Debug.Assert(Helper.IsSupportedEnumUnderlyingType(objectEnumType.UnderlyingType.PrimitiveTypeKind));
if (edmEnumType.UnderlyingType.PrimitiveTypeKind != objectEnumType.UnderlyingType.PrimitiveTypeKind)
{
throw new MappingException(
System.Data.Entity.Strings.Mapping_Enum_OCMapping_UnderlyingTypesMismatch(
edmEnumType.UnderlyingType.Name,
edmEnumType.FullName,
objectEnumType.UnderlyingType.Name,
objectEnumType.FullName));
}
// EnumMember.Value is just a number so sorting by value is faster than by the name.
// The drawback is that there can be multiple members with the same value. To break
// the tie we need to sort by name after sorting by value.
var edmEnumTypeMembersSortedEnumerator =
edmEnumType.Members.OrderBy(m => Convert.ToInt64(m.Value, CultureInfo.InvariantCulture)).ThenBy(m => m.Name).GetEnumerator();
var objectEnumTypeMembersSortedEnumerator =
objectEnumType.Members.OrderBy(m => Convert.ToInt64(m.Value, CultureInfo.InvariantCulture)).ThenBy(m => m.Name).GetEnumerator();
if (edmEnumTypeMembersSortedEnumerator.MoveNext())
{
while (objectEnumTypeMembersSortedEnumerator.MoveNext())
{
if (edmEnumTypeMembersSortedEnumerator.Current.Name == objectEnumTypeMembersSortedEnumerator.Current.Name &&
edmEnumTypeMembersSortedEnumerator.Current.Value.Equals(objectEnumTypeMembersSortedEnumerator.Current.Value))
{
if (!edmEnumTypeMembersSortedEnumerator.MoveNext())
{
return;
}
}
}
throw new MappingException(
System.Data.Entity.Strings.Mapping_Enum_OCMapping_MemberMismatch(
objectEnumType.FullName,
edmEnumTypeMembersSortedEnumerator.Current.Name,
edmEnumTypeMembersSortedEnumerator.Current.Value,
edmEnumType.FullName));
}
}
/// <summary>
/// Loads Association Type Mapping
/// </summary>
/// <param name="objectMapping"></param>
/// <param name="edmType"></param>
/// <param name="objectType"></param>
/// <param name="ocItemCollection"></param>
/// <param name="typeMappings"></param>
private static void LoadAssociationTypeMapping(ObjectTypeMapping objectMapping, EdmType edmType, EdmType objectType,
DefaultObjectMappingItemCollection ocItemCollection, Dictionary<string, ObjectTypeMapping> typeMappings)
{
Debug.Assert(edmType.BuiltInTypeKind == BuiltInTypeKind.AssociationType, "Expected Type Encountered in LoadAssociationTypeMapping");
Debug.Assert((edmType.BuiltInTypeKind == objectType.BuiltInTypeKind), "The BuiltInTypeKind must be same in LoadAssociationTypeMapping");
AssociationType association = (AssociationType)edmType;
AssociationType objectAssociation = (AssociationType)objectType;
foreach (AssociationEndMember edmEnd in association.AssociationEndMembers)
{
AssociationEndMember objectEnd = (AssociationEndMember)GetObjectMember(edmEnd, objectAssociation);
ValidateMembersMatch(edmEnd, objectEnd);
if (edmEnd.RelationshipMultiplicity != objectEnd.RelationshipMultiplicity)
{
throw new MappingException(System.Data.Entity.Strings.Mapping_Default_OCMapping_MultiplicityMismatch(
edmEnd.RelationshipMultiplicity, edmEnd.Name, association.FullName,
objectEnd.RelationshipMultiplicity, objectEnd.Name, objectAssociation.FullName));
}
Debug.Assert(edmEnd.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.RefType, "Ends must be of Ref type");
// GetMap for the entity types for the ends of the relationship type to make sure
// the entity type mentioned are valid
LoadTypeMapping(((RefType)edmEnd.TypeUsage.EdmType).ElementType,
((RefType)objectEnd.TypeUsage.EdmType).ElementType, ocItemCollection, typeMappings);
objectMapping.AddMemberMap(new ObjectAssociationEndMapping(edmEnd, objectEnd));
}
}
/// <summary>
/// The method loads the EdmMember mapping for complex members.
/// It goes through the CDM members of the Complex Cdm type and
/// tries to find the corresponding members in Complex Clr type.
/// </summary>
/// <param name="containingEdmMember"></param>
/// <param name="containingClrMember"></param>
/// <param name="ocItemCollection"></param>
/// <param name="typeMappings"></param>
/// <returns></returns>
private static ObjectComplexPropertyMapping LoadComplexMemberMapping(EdmProperty containingEdmMember, EdmProperty containingClrMember,
DefaultObjectMappingItemCollection ocItemCollection, Dictionary<string, ObjectTypeMapping> typeMappings)
{
Debug.Assert(containingEdmMember.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType, "edm member declaringType must be of complexType");
Debug.Assert(containingClrMember.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType, "clr member declaringType must be of complexType");
ComplexType edmComplexType = (ComplexType)containingEdmMember.TypeUsage.EdmType;
ComplexType objectComplexType = (ComplexType)containingClrMember.TypeUsage.EdmType;
// Get the type mapping for the complex type
ObjectTypeMapping complexTypeMapping = LoadTypeMapping(edmComplexType, objectComplexType, ocItemCollection, typeMappings);
//Go through the CDMMembers and find the corresponding member in Object space
//and create a member map.
return new ObjectComplexPropertyMapping(containingEdmMember, containingClrMember, complexTypeMapping);
}
private static ObjectTypeMapping LoadTypeMapping(EdmType edmType, EdmType objectType,
DefaultObjectMappingItemCollection ocItemCollection, Dictionary<string, ObjectTypeMapping> typeMappings)
{
ObjectTypeMapping objectTypeMapping;
//First, check in the type mappings to find out if the mapping is already present
if (typeMappings.TryGetValue(edmType.FullName, out objectTypeMapping))
{
return objectTypeMapping;
}
if (ocItemCollection != null)
{
ObjectTypeMapping typeMapping;
if (ocItemCollection.ContainsMap(edmType, out typeMapping))
{
return (ObjectTypeMapping)typeMapping;
}
}
// If the type mapping is not already loaded, then load it
return LoadObjectMapping(edmType, objectType, ocItemCollection, typeMappings);
}
private bool ContainsMap(GlobalItem cspaceItem, out ObjectTypeMapping map)
{
Debug.Assert(cspaceItem.DataSpace == DataSpace.CSpace, "ContainsMap: It must be a CSpace item");
int index;
if (cdmTypeIndexes.TryGetValue(cspaceItem.Identity, out index))
{
map = (ObjectTypeMapping)this[index];
return true;
}
map = null;
return false;
}
#endregion
}
}
|